使用函数的查询比使用子选择的相同查询花费的时间更长
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 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
,因为它不是;错误声明非确定性函数可能会导致意外结果。
我有一个功能可以为客户获取余额。
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 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
,因为它不是;错误声明非确定性函数可能会导致意外结果。