根据先前和实际的行值计算行值


9

大家好,谢谢您的帮助。
我遇到以下情况:称为语句的表,其中包含字段id(int),stmnt_date(date),借方(double),贷方(double)和余额(double) 我的桌子的结构

我想按照以下规则计算余额:

第一行余额(按时间顺序)=借方-贷方,其余各行

当前行余额=按时间顺序上一个行余额+当前行借方-当前行贷方

正如您在上面的图片中看到的,行没有按日期排列,这就是为什么我按时间顺序两次使用单词来强调stmnt_date值的重要性的原因。

非常感谢您的帮助。


您是否可以将借方和贷方字段合并为一个字段?如果是这样,则可以将负值用作借方,将正值用作贷方。
麦克(Mike)

1
对于以后的问题(因为已经回答了此问题),请以文本而非印刷屏幕的形式发布邮政编码。还包括CREATE TABLE语句和示例数据(带有INSERT)。
ypercubeᵀᴹ

为了向您的答案@ypercube致敬,对于阅读此书的任何人,我在dba.stackexchange.com/a/183207/131900
Zack Morris

Answers:


8

假设stmnt_date有一个UNIQUE约束,使用窗口/分析函数将很容易:

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 ;

不幸的是,MySQL还没有实现分析功能。您可以使用严格的SQL,通过自动联接表(虽然工作100%应该效率不高)或使用特定的MySQL功能,变量(效率很高,但是必须对其进行测试)来解决问题升级mysql时,请确保结果仍然正确,并且未因某些优化改进而受损):

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 ;

使用您的数据,将导致:

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

我认为您可以尝试以下方法:

set @balance := 0;

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

2

ypercube的答案非常出色(我从未见过通过这样的伪选择在单个查询中创建变量),因此为方便起见,这里是CREATE TABLE语句。

对于Google Image Search中的表格数据图像,可以使用https://convertio.co/ocr/https://ocr.space/将其转换为文本文档。然后,如果OCR无法正确检测到列,并且您使用的是Mac,请使用TextWrangler,同时按住选项键以执行矩形选择并四处移动列。SQL编辑器(例如Sequel Pro,TextWrangler)和电子表格(例如Google Docs)的结合使处理以制表符分隔的表格数据极为高效。

如果可以把所有这些都发表在评论中,那么请不要反对这个答案。

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

自联接表在大型表上不是很快。因此,在PostgreSQL上处理此任务时,我决定使用触发器函数来计算存储的字段“ balance”。每行仅进行一次所有计算。

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

例如,在MSSQL中:

使用with()语句生成CTE。这实际上是一个临时结果集,将显示每行的值。您可以在with语句中使用数学运算在末尾创建一列,使用数学运算显示行的总数为DEBIT-CREDIT。在with语句中,您需要为每行分配行号,使用WITH()的OVER子句按stmnt_date进行排序。

然后,使用a.ROWNUMBER = b.ROWNUMBER-1或+1将表递归连接到自身上,这将允许您引用此行和上一行的a.total + b.total =总数。

我感谢我没有提供代码,但这是实现此目的的实用方法。如果需要,我可以提供代码:)


1
问题是关于MySQL。提供代码说明如何使用CTE或具有DBMS的DBMS中的窗口函数(如Postgres,SQL-Server,DB2,Oracle等)来完成此工作很不错(相反),但是应该至少提供有关如何在MySQL中执行此操作的代码。
ypercubeᵀᴹ
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.