使用函数的查询比使用子选择的相同查询花费的时间更长

Query with function taking longer than same query with subselect

我有一个功能可以为客户获取余额。

CREATE OR REPLACE FUNCTION default1.get_balance (par_customer_id DECIMAL(31, 0))
  RETURNS DECIMAL(31,15)
  LANGUAGE SQL
  DETERMINISTIC
  NO EXTERNAL ACTION
  READS SQL DATA
BEGIN

  DECLARE var_balance DECIMAL(31,15); 

  SELECT SUM(amount)
  INTO var_balance
  FROM default1.accounting accounting 
  WHERE accounting.customer_id  = par_customer_id
    AND (YEAR(accounting.accounting_date) >= YEAR(SYSDATE)-3 or accounting.accounting_date IS NULL)
    AND paid_date IS NULL
    AND accounting_type_id <> 2
    AND NOT EXISTS (
        SELECT 1 
        FROM default1.accounting_detail detail 
        WHERE accounting.id = detail.accounting_id 
        AND detail.paid_date IS NOT NULL);

  RETURN var_balance;

END

获取一个客户的余额的性能很好,但是使用查询中的函数一次获取多个客户的余额会变得非常慢。

SELECT default1.get_balance(customer.id), customer.*
FROM default1.customer customer
WHERE customer.id < 1000

执行此查询需要 2 分钟多的时间。

当我用子选择替换查询中的函数时,速度要快得多。

SELECT 
  (SELECT SUM(amount)
    FROM default1.accounting accounting
    WHERE accounting.customer_id = customer.id
    AND (YEAR(accounting.accounting_date) >= YEAR(SYSDATE)-3 or accounting.accounting_date IS NULL)
    AND paid_date IS NULL
    AND accounting_type_id <> 2
    AND NOT EXISTS (
        SELECT 1 
        FROM default1.accounting_detail detail 
        WHERE accounting.id = detail.accounting_id 
        AND detail.paid_date IS NOT NULL)),
  customer.*
FROM 
    default1.customer customer
WHERE customer.id < 1000

此查询大约需要 8 秒。

我确实以不同的顺序多次执行了这两个查询,而运行时没有任何重大变化。所以我认为这不是缓存问题。

为什么使用 function 的查询比使用 subselect 的查询花费大约 15 倍的时间?
我可以更改函数以使其更快吗?

我假设 LUW 是 DB2。

您的函数的性能可能会受到影响,因为它使用编译的复合语句作为其主体 (BEGIN ... END)。尝试使用 inlined compound statementBEGIN ATOMIC ... END。更好的是,您可以只使用 RETURN 语句:

CREATE OR REPLACE FUNCTION default1.get_balance (par_customer_id DECIMAL(31, 0))
  RETURNS DECIMAL(31,15)
  LANGUAGE SQL
  NOT DETERMINISTIC
  NO EXTERNAL ACTION
  READS SQL DATA
RETURN SELECT SUM(amount)
  INTO var_balance
  FROM default1.accounting accounting 
  WHERE accounting.customer_id  = par_customer_id
    AND (YEAR(accounting.accounting_date) >= YEAR(SYSDATE)-3 or accounting.accounting_date IS NULL)
    AND paid_date IS NULL
    AND accounting_type_id <> 2
    AND NOT EXISTS (
        SELECT 1 
        FROM default1.accounting_detail detail 
        WHERE accounting.id = detail.accounting_id 
        AND detail.paid_date IS NOT NULL);

当使用编译复合语句时,每次调用函数都会导致上下文从 SQL 数据访问引擎切换到 PSM 执行引擎并返回,而内联语句成为一部分查询计划本身。

请注意,您不应将此函数声明为 DETERMINISTIC,因为它不是;错误声明非确定性函数可能会导致意外结果。