如何提高计算 运行 总数的 select 语句的性能

How to improve performance of a select statement, which calculates a running total

我正在使用 Firebird 2.5.2 并试图找出如何提高 select 语句的性能,该语句计算 运行 总数。情况是这样的:

A table“PAYMENTS”(为了更好的概览而进行了简化)

CREATE TABLE PAYMENTS 
(
  ID                    INTEGER             NOT NULL,
  PAYMENT_TYPE          SMALLINT            DEFAULT 0,
  BANKING_ACCOUNTID     INTEGER             DEFAULT -100,
  AMOUNT                DOUBLE PRECISION,
  CALCAMOUNT            DOUBLE PRECISION,
  PAYMENT_DATE          TIMESTAMP
);

ALTER TABLE PAYMENTS ADD CONSTRAINT PK_PAYMENTS PRIMARY KEY (ID);
CREATE GENERATOR PAYMENTS_PRIMARYKEY;

CREATE ASC INDEX PAYMENTS_AMOUNT ON PAYMENTS (AMOUNT);
CREATE ASC INDEX PAYMENTS_BANKACCOUNT ON PAYMENTS (BANKING_ACCOUNTID);
CREATE ASC INDEX PAYMENTS_BDATE ON PAYMENTS (PAYMENT_DATE);

ALTER TABLE PAYMENTS ADD CONSTRAINT FK_PAYM_BANKING_ACCOUNTID FOREIGN KEY (BANKING_ACCOUNTID) REFERENCES BANKINGACCOUNTS (ID) ON UPDATE CASCADE ON DELETE SET DEFAULT;

“金额”中的值始终为正数。无论是收款还是付款,都是通过“PAYMENT_TYPE”定义的。两者都确定“CALCAMOUNT”中的值,即出于计算原因,此处的值是“-100”或“100”。

计算总和的存储过程:

SET TERM  ^^ ;
create procedure SP_BALANCE_FOR_DATE_AND_BANKID (PAYMENT_DATE timestamp, BANKING_ACCOUNTID integer) returns (BALANCE double precision)
as
declare variable TempAmount double precision;
begin
    TempAmount = 0;
 
    select sum(CALCAMOUNT) as SUMME from PAYMENTS where (PAYMENT_DATE < :PAYMENT_DATE) and (BANKING_ACCOUNTID = :BANKING_ACCOUNTID) into TempAmount;
    if (TempAmount is null) then TempAmount = 0;
     
    BALANCE = TempAmount;
    
    suspend;
end
 ^^
SET TERM ;  ^^

实际select语句:

select PAYMENTS.*,
case when PAYMENT_TYPE = 1 then ((select BALANCE from SP_BALANCE_FOR_DATE_AND_BANKID(PAYMENT_DATE, BANKING_ACCOUNTID)) + AMOUNT)
else ((select BALANCE from SP_BALANCE_FOR_DATE_AND_BANKID(PAYMENT_DATE, BANKING_ACCOUNTID)) - AMOUNT) end
as CURRENTBALANCE
from PAYMENTS
where (ID > 0) and (BANKING_ACCOUNTID = :BANKING_ACCOUNTID) and ((PAYMENT_TYPE = 1) or (PAYMENT_TYPE = 2)) and (PAYMENT_DATE >= '01.11.2021') and (PAYMENT_DATE <= '30.11.2021 23:59:59')
order by PAYMENT_DATE

用户可以定义银行账户和日期范围,结果显示在网格中,除了“PAYMENTS”的所有字段外,还显示“CURRENTBALANCE”字段,显示余额/总计在 之后适当付款。无论要显示的记录的定义日期范围如何,都必须为该记录计算一条记录之前的 所有 条记录(基于 PAYMENT_DATE)。

问题:很慢,记录越多

我在发帖之前进行了搜索,发现了以下解决方案:

前两个的性能差不多,就是很慢。在测试之前,我也尝试过添加更多的索引。

显然,第三个只有 Firebird 3.0 才有可能。由于我仍在使用 Firebird 2.5,我想问一下,如何提高 select 语句的性能?

当然可以选择在存储每条记录后直接存储适当的总数,但这里的问题是,一旦付款被删除或修改,删除/修改后所有记录的总数一个变得不正确。

您应该将整个查询放入存储过程中,在一个简单的循环中计算并返回总数 运行。这将是最快的方法。

像这样:

CREATE PROCEDURE RUNNING_BALANCE(BIGINT ACCOUNTID, DATE_BEGIN DATE, DATE_END DATE)
RETURNS (BALANCE DOUBLE PRECISION)
AS
DECLARE t SMALLINT;
DECLARE a DOUBLE PRECISION;
BEGIN
 BALANCE = 0;

 select sum(CALCAMOUNT) from PAYMENTS
  where (PAYMENT_DATE < :DATE_BEGIN)
   and (BANKING_ACCOUNTID = :ACCOUNTID)
  into :BALANCE;

 FOR select PAYMENT_TYPE, AMOUNT from PAYMENTS
   where (ID > 0)
    and (BANKING_ACCOUNTID = :ACCOUNTID)
    and ((PAYMENT_TYPE = 1) or (PAYMENT_TYPE = 2))
    and (PAYMENT_DATE >= :DATE_BEGIN)
    and (PAYMENT_DATE < :DATE_END + 1)
   order by PAYMENT_DATE
  INTO :t, :a DO
  BEGIN
   IF (t = 1) THEN
     BALANCE = BALANCE + a;
   ELSE
     BALANCE = BALANCE - a;
   SUSPEND;
  END
END