Tính giá trị hàng dựa trên giá trị hàng trước đó và thực tế


9

Chào mọi người và cảm ơn sự giúp đỡ của bạn.
Tôi có tình huống sau: một bảng được gọi là các câu lệnh chứa các trường id (int), stmnt_date (ngày), ghi nợ (gấp đôi), tín dụng (gấp đôi) và số dư (gấp đôi) Cấu trúc bảng của tôi

Tôi muốn tính số dư theo các quy tắc sau:

Số dư hàng đầu tiên ( theo thời gian ) = ghi nợ - tín dụng và cho các hàng còn lại

số dư hàng hiện tại = số dư hàng trước theo thời gian + ghi nợ hàng hiện tại - tín dụng hàng hiện tại

Như bạn có thể thấy trên hình trên, các hàng không được sắp xếp theo ngày và đó là lý do tại sao tôi sử dụng từ theo thứ tự thời gian hai lần để nhấn mạnh tầm quan trọng của giá trị stmnt_date.

Cảm ơn bạn rất nhiều vì đã giúp đỡ của bạn.


Bạn có thể tham gia các lĩnh vực ghi nợ và tín dụng vào một lĩnh vực không? Nếu vậy, thì bạn có thể sử dụng các giá trị âm làm giá trị ghi nợ và giá trị dương làm tín dụng.
Mike

1
Đối với các câu hỏi trong tương lai (vì điều này đã được trả lời), hãy đăng mã bằng văn bản, không phải màn hình in. Cũng bao gồm các CREATE TABLEbáo cáo và dữ liệu mẫu (với INSERT).
ypercubeᵀᴹ

Để tỏ lòng tôn kính với câu trả lời của bạn @ypercube, đối với bất kỳ ai đang đọc bài này, tôi đã thêm một ví dụ CREATE TABLE và INSERT bên dưới dba.stackexchange.com/a/183207/131900
Zack Morris

Câu trả lời:


8

Giả sử stmnt_datecó một UNIQUEràng buộc, điều này sẽ khá dễ dàng với các hàm cửa sổ / phân tích:

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 ;

Thật không may, MySQL không (chưa) đã thực hiện các chức năng phân tích. Bạn có thể giải quyết vấn đề bằng SQL nghiêm ngặt, bằng cách tự tham gia bảng (điều này khá kém hiệu quả mặc dù hoạt động 100%) hoặc bằng cách sử dụng một tính năng cụ thể của MySQL, các biến (sẽ khá hiệu quả nhưng bạn phải kiểm tra nó khi nâng cấp mysql, để chắc chắn rằng kết quả vẫn chính xác và không bị sai lệch bởi một số cải tiến tối ưu hóa):

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 ;

Với dữ liệu của bạn, nó sẽ dẫn đến:

+------------+-------+--------+---------+
| 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

Tôi nghĩ bạn có thể thử như sau:

set @balance := 0;

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

2

Câu trả lời của ypercube khá ngoạn mục (tôi chưa bao giờ thấy một sáng tạo biến trong một truy vấn duy nhất thông qua một lựa chọn giả như thế), vì vậy đây là câu lệnh CREATE TABLE để thuận tiện cho bạn.

Đối với hình ảnh dữ liệu dạng bảng trong Tìm kiếm hình ảnh của Google, bạn có thể sử dụng https://convertio.co/oc/ hoặc https: // nob.space/ để chuyển đổi nó thành tài liệu văn bản. Sau đó, nếu OCR không phát hiện đúng các cột và bạn có máy Mac, hãy sử dụng TextWrangler với phím tùy chọn được giữ để thực hiện lựa chọn hình chữ nhật và di chuyển các cột xung quanh. Sự kết hợp của trình soạn thảo SQL như Sequel Pro , TextWrangler và bảng tính như Google Docs giúp việc xử lý dữ liệu bảng được phân tách bằng tab cực kỳ hiệu quả.

Nếu tôi có thể đưa tất cả những điều này vào một bình luận thì tôi sẽ không đưa ra câu trả lời này.

-- 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

Các bảng tự tham gia không nhanh lắm trên các bảng lớn. Vì vậy, để xử lý tác vụ này trên PostgreSQL, tôi quyết định sử dụng hàm kích hoạt để tính toán "số dư" của trường được lưu trữ. Tất cả các tính toán chỉ xảy ra một lần cho mỗi hàng.

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

Trong, ví dụ, MSSQL:

Sử dụng câu lệnh with () để tạo CTE. Đây thực chất là một tập kết quả tạm thời sẽ hiển thị giá trị của mỗi hàng. Bạn có thể sử dụng toán học trong câu lệnh with để tạo một cột ở cuối, sử dụng toán học để hiển thị tổng số hàng là DEBIT-CREDIT. Trong câu lệnh của bạn, bạn sẽ cần gán số hàng cho mỗi hàng, sử dụng mệnh đề QUÁN của VỚI () để đặt hàng theo stmnt_date.

Sau đó, đệ quy nối bảng vào chính nó, sử dụng a.ROWNUMBER = b.ROWNUMBER-1 hoặc +1 sẽ cho phép bạn tham chiếu a.total + b.total = tổng của hàng này và hàng trước đó.

Tôi đánh giá cao tôi không cung cấp mã tuy nhiên đây là phương pháp thực tế để đạt được điều này. Tôi có thể cung cấp mã nếu được yêu cầu :)


1
Câu hỏi là về MySQL. Mặc dù nó không tệ (ngược lại) để cung cấp mã về cách thực hiện điều này với các CTE hoặc các hàm cửa sổ trong DBMS có nó (như Postgres, SQL-Server, DB2, Oracle, ... danh sách dài) ít nhất nên cung cấp mã về cách làm điều này trong MySQL.
ypercubeᵀᴹ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.