【发布时间】:2011-05-10 13:59:48
【问题描述】:
我在 1 分钟的查询和表值函数中的相同查询之间存在巨大的执行时间差异。
但最奇怪的是使用另一个(有效的)company_id 参数运行 UDF 在约 40 秒内给我一个结果,一旦我将此 company_id 更改为 12(再次有效),它就永远不会停止。 这两个查询的执行计划绝对不一样,当然长的那个是最复杂的。但是批处理版本和UDF版本之间的执行计划是一样的,而且批处理版本很快……!
如果我“手动”执行以下查询,则执行时间为 1min36s,包含 306 行:
SELECT
dbo.date_only(Call.date) AS date,
count(DISTINCT customer_id) AS new_customers
FROM
Call
LEFT OUTER JOIN
dbo.company_new_customers(12, 2009, 2009) new_customers
ON dbo.date_only(new_customers.date) = dbo.date_only(Call.date)
WHERE
company_id = 12
AND year(Call.date) >= 2009
AND year(Call.date) <= 2009
GROUP BY
dbo.date_only(Call.date)
我将这个完全相同的查询存储在一个函数中并像这样运行它:
SELECT * FROM company_new_customers_count(12, 2009, 2009)
现在它正在运行 13 分钟......而且我确信它永远不会给我任何结果。
昨天,我在超过 4 小时内出现了完全相同的类似无限循环的行为(所以我停止了它)。
这里是函数的定义:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION company_new_customers_count
(
@company_id int,
@start_year int,
@end_year int
)
RETURNS TABLE
AS
RETURN
(
SELECT
dbo.date_only(Call.date) AS date,
count(DISTINCT customer_id) AS new_customers
FROM
Call
LEFT OUTER JOIN
dbo.company_new_customers(@company_id, @start_year, @end_year) new_customers
ON dbo.date_only(new_customers.date) = dbo.date_only(Call.date)
WHERE
company_id = @company_id
AND year(Call.date) >= @start_year
AND year(Call.date) <= @end_year
GROUP BY
dbo.date_only(Call.date)
)
GO
我很乐意了解发生了什么。
谢谢
补充:
company_new_customers 的定义:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Description: Create the list of new customers of @company_id
-- in the given period.
-- =============================================
CREATE FUNCTION company_new_customers
(
@company_id int,
@start_year int,
@end_year int
)
RETURNS TABLE
AS
RETURN
(
SELECT
customer_id,
date
FROM
( -- select apparition dates of cutomers before @end_year
SELECT
min(date) AS date,
customer_id
FROM
Call
JOIN
Call_Customer ON Call_Customer.call_id = Call.call_id
WHERE
company_id = @company_id
AND year(date) <= @end_year
GROUP BY
customer_id
) new_customers
WHERE
year(date) >= @start_year -- select apparition dates of cutomers after @start_year
)
GO
date_only 的定义:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: Julio Guerra
-- Create date: 14/10/2010
-- Description: Return only the date part of a datetime value
-- Example: date_only('2010-10-25 13:00:12') returns 2010-10-25
-- =============================================
CREATE FUNCTION date_only
(
@datetime datetime
)
RETURNS datetime
AS
BEGIN
RETURN dateadd(dd, 0, datediff(dd, 0, @datetime))
END
GO
SELECT * FROM company_new_customers_count(8, 2009, 2009)的执行计划
SELECT * FROM company_new_customers_count(12, 2009, 2009)的执行计划
【问题讨论】:
-
所有其他函数的定义是什么:dbo.company_new_customers、dbo.date_only?这些表的大小/索引是多少?
-
@mellamok : 在帖子末尾添加。
-
2h26 和它仍在运行的最后一个执行计划......
-
“2h26 和它仍在运行的最后一个执行计划” - 听起来它处于锁定等待状态。
标签: sql sql-server sql-server-2005 tsql user-defined-functions