Hitung nilai baris berdasarkan nilai baris sebelumnya dan aktual

9

Hai semuanya dan terima kasih atas bantuan Anda.
Saya memiliki situasi berikut: tabel yang disebut pernyataan yang berisi id bidang (int), stmnt_date (tanggal), debit (dua kali lipat), kredit (dua kali lipat) dan saldo (dua kali lipat) Struktur meja saya

Saya ingin menghitung saldo mengikuti aturan ini:

Saldo baris pertama ( secara kronologis ) = debit - kredit dan untuk sisa baris

saldo baris saat ini = saldo baris sebelumnya secara kronologis + debit baris saat ini - kredit baris saat ini

Seperti yang Anda lihat pada gambar di atas, baris tidak diatur berdasarkan tanggal dan itulah sebabnya saya menggunakan kata secara kronologis dua kali untuk menekankan pentingnya nilai stmnt_date.

Terima kasih banyak atas bantuan Anda.

Mohamed Anis Dahmani
sumber
Apakah Anda dapat menggabungkan bidang debit dan kredit ke dalam satu bidang? Jika demikian, maka Anda dapat menggunakan nilai negatif sebagai debit dan nilai positif sebagai kredit.
Mike
1
Untuk pertanyaan selanjutnya (karena ini telah dijawab), poskan kode dalam teks, bukan layar cetak. Juga sertakan CREATE TABLEpernyataan dan data sampel (dengan INSERT).
ypercubeᵀᴹ
Sebagai penghormatan atas jawaban Anda @ypercube, untuk siapa pun yang membaca ini saya menambahkan contoh CREATE TABLE dan INSERT di bawah ini dba.stackexchange.com/a/183207/131900
Zack Morris

Jawaban:

8

Dengan asumsi yang stmnt_datememiliki UNIQUEkendala, ini akan cukup mudah dengan fungsi jendela / analitik:

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 ;

Sayangnya, MySQL belum (belum) mengimplementasikan fungsi analitik. Anda dapat memecahkan masalah baik dengan SQL ketat, dengan bergabung sendiri tabel (yang seharusnya agak tidak efisien meskipun bekerja 100%) atau dengan menggunakan fitur MySQL spesifik, variabel (yang akan sangat efisien tetapi Anda harus mengujinya ketika memutakhirkan mysql, untuk memastikan bahwa hasilnya masih benar dan tidak rusak oleh beberapa peningkatan optimasi):

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 ;

Dengan data Anda, itu akan menghasilkan:

+------------+-------+--------+---------+
| 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)
ypercubeᵀᴹ
sumber
6

Saya pikir Anda bisa mencoba yang berikut:

set @balance := 0;

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

jawaban ypercube sangat spektakuler (saya belum pernah melihat kreasi variabel dalam satu permintaan melalui pilihan boneka seperti itu), jadi inilah pernyataan CREATE TABLE untuk kenyamanan Anda.

Untuk gambar data tabular dalam Google Image Search, Anda dapat menggunakan https://convertio.co/ocr/ atau https://ocr.space/ untuk mengubahnya menjadi dokumen teks. Kemudian jika OCR tidak mendeteksi kolom dengan benar, dan Anda memiliki Mac, gunakan TextWrangler dengan tombol pilihan ditekan untuk melakukan pemilihan persegi panjang dan memindahkan kolom di sekitar. Kombinasi dari SQL editor seperti Sequel Pro , TextWrangler dan spreadsheet seperti Google Docs membuat berurusan dengan data tabular yang dipisahkan dengan tab menjadi sangat efisien.

Jika saya bisa memasukkan semua ini ke dalam komentar yang saya mau, jadi tolong jangan mundur jawaban ini.

-- 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)
*/
Zack Morris
sumber
1

Bergabung sendiri tabel itu tidak terlalu cepat di meja besar. Jadi berurusan dengan tugas ini pada PostgreSQL saya memutuskan untuk menggunakan fungsi pemicu untuk menghitung "keseimbangan" bidang yang tersimpan. Semua perhitungan hanya terjadi satu kali untuk setiap baris.

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;
QuickJoe
sumber
-1

Dalam, misalnya, MSSQL:

Gunakan pernyataan with () untuk menghasilkan CTE. Ini pada dasarnya adalah himpunan hasil sementara yang akan menunjukkan nilai setiap baris. Anda dapat menggunakan matematika dalam pernyataan with untuk membuat kolom di bagian akhir, menggunakan matematika untuk menunjukkan total baris adalah DEBIT-CREDIT. Dalam pernyataan with Anda, Anda harus menetapkan nomor baris untuk setiap baris, gunakan klausa OVER DENGAN () untuk memesan oleh stmnt_date.

Kemudian, secara rekursif gabung tabel ke dirinya sendiri, menggunakan a.ROWNUMBER = b.ROWNUMBER-1 atau +1 yang akan memungkinkan Anda untuk merujuk a.total + b.total = total baris ini dan baris sebelumnya.

Saya menghargai saya tidak memberikan kode namun ini adalah metode praktis untuk mencapai ini. Saya dapat memberikan kode jika diminta :)

chrlsuk
sumber
1
Pertanyaannya adalah tentang MySQL. Meskipun tidak buruk (sebaliknya) untuk memberikan kode tentang bagaimana hal ini dapat dilakukan dengan CTE atau fungsi jendela dalam DBMS yang memilikinya (seperti Postgres, SQL-Server, DB2, Oracle, ... daftarnya panjang), Anda setidaknya harus memberikan kode tentang cara melakukan ini di MySQL.
ypercubeᵀᴹ