通过子选择

时间:2017-07-14 10:49:35

标签: sql db2 query-performance stored-functions db2-luw

我有一个功能来为客户获得余额。

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秒钟。

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

为什么使用该函数的查询比使用subselect的查询长约15倍? 我可以在功能上做些什么来改变它吗?

1 个答案:

答案 0 :(得分:2)

我假设DB2 for LUW。

您的函数的性能可能会受到影响,因为它使用编译的复合语句作为其主体(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,因为它不是;错误地声明非确定性函数可能会导致意外结果。