Önceki ve gerçek satır değerlerine dayalı olarak satır değerini hesaplama


9

Herkese merhaba ve yardımlarınız için teşekkürler.
Aşağıdaki durum var: alanlar id (int), stmnt_date (tarih), borç (çift), kredi (çift) ve bakiye (çift) içeren deyimler adlı bir tabloMasamın yapısı

Bu kuralları takip ederek dengeyi hesaplamak istiyorum:

İlk satır bakiyesi ( kronolojik olarak ) = borç - kredi ve diğer satırlar için

geçerli satır bakiyesi = kronolojik olarak önceki satır bakiyesi + geçerli satır ödemesi - geçerli satır kredisi

Yukarıdaki resimde gördüğünüz gibi satırlar tarihe göre düzenlenmemiştir ve bu yüzden stmnt_date değerinin önemini vurgulamak için kelimeyi kronolojik olarak iki kez kullandım.

Yardımın için çok teşekkürler.


Borç ve alacak alanlarını tek bir alana katabiliyor musunuz? Öyleyse, negatif değerleri borç olarak ve pozitif değerleri kredi olarak kullanabilirsiniz.
Mike

1
Gelecekteki sorular için (bu yanıtlandığından beri), baskı ekranlarında değil, metin içinde posta kodu. Ayrıca CREATE TABLEifadeleri ve örnek verileri (ile INSERT) de ekleyin .
ypercubeᵀᴹ

Cevabınıza @ypercube saygıyla, bunu okuyan herkes için aşağıya bir CREATE TABLE ve INSERT örneği ekledim dba.stackexchange.com/a/183207/131900
Zack Morris

Yanıtlar:


8

Varsayarsak stmnt_datebir sahiptir UNIQUEkısıtlamasını, bu pencere / analitik fonksiyonları ile oldukça kolay olacaktır:

SELECT 
    s.stmnt_date, s.debit, s.credit,
    SUM(s.debit - s.credit) OVER (ORDER BY s.stmnt_date
                                  ROWS BETWEEN UNBOUNDED PRECEDING
                                           AND CURRENT ROW)
        AS balance
FROM
    statements AS s
ORDER BY
    stmnt_date ;

Ne yazık ki, MySQL (henüz) analitik fonksiyonlar uygulamamıştır. Sorunu katı SQL'de, tabloya kendiliğinden katılarak (% 100 çalışmasına rağmen oldukça verimsiz olması gerekir) veya belirli bir MySQL özelliği, değişkenler (oldukça verimli olurdu, ancak test etmeniz gerekir) ile sorunu çözebilirsiniz. MySQL'i yükseltirken, sonuçların hala doğru olduğundan ve bazı optimizasyon iyileştirmeleri tarafından yönetilmediğinden emin olmak için):

SELECT 
    s.stmnt_date, s.debit, s.credit,
    @b := @b + s.debit - s.credit AS balance
FROM
    (SELECT @b := 0.0) AS dummy 
  CROSS JOIN
    statements AS s
ORDER BY
    stmnt_date ;

Verilerinizle sonuçlanır:

+------------+-------+--------+---------+
| stmnt_date | debit | credit | balance |
+------------+-------+--------+---------+
| 2014-05-15 |  3000 |      0 |    3000 |
| 2014-06-17 | 20000 |      0 |   23000 |
| 2014-07-16 |     0 |   3000 |   20000 |
| 2014-08-14 |     0 |   3000 |   17000 |
| 2015-02-01 |  3000 |      0 |   20000 |
+------------+-------+--------+---------+
5 rows in set (0.00 sec)

6

Sanırım aşağıdakileri deneyebilirsiniz:

set @balance := 0;

SELECT stmnt_date, debit, credit, (@balance := @balance + (debit - credit)) as Balance
FROM statements
ORDER BY stmnt_date;

2

ypercube cevap oldukça muhteşem (Ben hiç böyle bir kukla seçim yoluyla tek bir sorgu içinde değişken bir oluşturma görmedim), işte size kolaylık sağlamak için CREATE TABLE ifadesi.

Google Görsel Arama'daki sekmeli veri resimleri için, bunu bir metin belgesine dönüştürmek üzere https://convertio.co/ocr/ veya https://ocr.space/ adresini kullanabilirsiniz . Sonra OCR sütunları düzgün algılamadıysa ve bir Mac'iniz varsa, dikdörtgen bir seçim yapmak ve sütunları hareket ettirmek için seçenek tuşunu basılı tutarak TextWrangler'ı kullanın . Sequel Pro , TextWrangler gibi SQL editörünün ve Google Dokümanlar gibi bir e-tablonun kombinasyonu, sekmeyle ayrılmış sekmeli verilerle ilgilenmeyi son derece verimli hale getirir.

Tüm bunları bir yorumda yazabilirsem, lütfen bu cevabı iptal etmeyin.

-- DROP TABLE statements;

CREATE TABLE IF NOT EXISTS statements (
  id integer NOT NULL AUTO_INCREMENT,
  stmnt_date date,
  debit integer not null default 0,
  credit integer not null default 0,
  PRIMARY KEY (id)
);

INSERT INTO statements
(stmnt_date  , debit, credit) VALUES
('2014-06-17', 20000, 0     ),
('2014-08-14', 0    , 3000  ),
('2014-07-16', 0    , 3000  ),
('2015-02-01', 3000 , 0     ),
('2014-05-15', 3000 , 0     );

-- this is slightly modified from ypercube's (@b := 0 vs @b := 0.0)
SELECT 
    s.stmnt_date, s.debit, s.credit,
    @b := @b + s.debit - s.credit AS balance
FROM
    (SELECT @b := 0) AS dummy 
CROSS JOIN
    statements AS s
ORDER BY
    stmnt_date ASC;

/* result
+------------+-------+--------+---------+
| stmnt_date | debit | credit | balance |
+------------+-------+--------+---------+
| 2014-05-15 |  3000 |      0 |    3000 |
| 2014-06-17 | 20000 |      0 |   23000 |
| 2014-07-16 |     0 |   3000 |   20000 |
| 2014-08-14 |     0 |   3000 |   17000 |
| 2015-02-01 |  3000 |      0 |   20000 |
+------------+-------+--------+---------+
5 rows in set (0.00 sec)
*/

1

Kendiliğinden katılan masalar büyük masalarda çok hızlı değil. Bu yüzden PostgreSQL'de bu görevle uğraşarak saklı alan "bakiyesini" hesaplamak için tetik fonksiyonunu kullanmaya karar verdim. Tüm hesaplamalar her satır için yalnızca bir kez yapılır.

DROP TABLE IF EXISTS statements;

CREATE TABLE IF NOT EXISTS statements (
  id BIGSERIAL,
  stmnt_date TIMESTAMP,
  debit NUMERIC(18,2) not null default 0,
  credit NUMERIC(18,2) not null default 0,
  balance NUMERIC(18,2)
);

CREATE OR REPLACE FUNCTION public.tr_fn_statements_balance()
RETURNS trigger AS
$BODY$
BEGIN

    UPDATE statements SET
    balance=(SELECT SUM(a.debit)-SUM(a.credit) FROM statements a WHERE a.stmnt_date<=statements.stmnt_date)
    WHERE stmnt_date>=NEW.stmnt_date;

RETURN NULL;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

CREATE TRIGGER tr_statements_after_update
  AFTER INSERT OR UPDATE OF debit, credit
  ON public.statements
  FOR EACH ROW
  EXECUTE PROCEDURE public.tr_fn_statements_balance();


INSERT INTO statements
(stmnt_date  , debit, credit) VALUES
('2014-06-17', 20000, 0     ),
('2014-08-14', 0    , 3000  ),
('2014-07-16', 0    , 3000  ),
('2015-02-01', 3000 , 0     ),
('2014-05-15', 3000 , 0     );


select * from statements order by stmnt_date;

-1

Örneğin, MSSQL'de:

CTE oluşturmak için with () deyimini kullanın. Bu aslında her satırın değerini gösterecek geçici bir sonuç kümesidir. Sonunda bir sütun oluşturmak için with ifadesindeki math komutunu, satırın toplamının DEBIT-CREDIT olduğunu göstermek için math komutunu kullanabilirsiniz. With ifadenizde, her satıra satır numaraları atamanız gerekir, stmnt_date'ye göre sipariş vermek için WITH () öğesinin OVER yan tümcesini kullanın.

Daha sonra, bu satırın ve bir önceki satırın a.total + b.total = toplamını göndermenizi sağlayacak bir.ROWNUMBER = b.ROWNUMBER-1 veya +1 kullanarak tabloyu tekrar tekrar birleştirin.

Kod sağlamak değil ancak bu ulaşmak için pratik bir yöntem olduğunu takdir ediyorum. İstenirse kod sağlayabilirim :)


1
Soru MySQL ile ilgili. (Aksine) bunun BTMS'ler veya DBMS'deki pencere işlevleriyle (Postgres, SQL-Server, DB2, Oracle, ... liste uzun) nasıl yapılabileceğinin kodunu sağlamak kötü olmasa da, en azından MySQL'de bunun nasıl yapılacağı hakkında kod sağlamalıdır.
ypercubeᵀᴹ
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.