我有一个功能来为客户获得余额。
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倍? 我可以在功能上做些什么来改变它吗?
答案 0 :(得分:2)
我假设DB2 for LUW。
您的函数的性能可能会受到影响,因为它使用编译的复合语句作为其主体(BEGIN ... END
)。尝试使用inlined compound statement:BEGIN 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
,因为它不是;错误地声明非确定性函数可能会导致意外结果。