Sql 带有递归CTE的函数似乎非常慢
我目前正在开发一个使用递归CTE的函数,但它的性能似乎很差。我需要这是在功能(所以没有临时表),所以我可以很容易地在存储过程中使用它 代码如下:Sql 带有递归CTE的函数似乎非常慢,sql,sql-server-2005,tsql,common-table-expression,Sql,Sql Server 2005,Tsql,Common Table Expression,我目前正在开发一个使用递归CTE的函数,但它的性能似乎很差。我需要这是在功能(所以没有临时表),所以我可以很容易地在存储过程中使用它 代码如下: CREATE FUNCTION [dbo].[Web_GetDailyLoadListUDF] ( @CustomerID INT , @StartDate DATETIME , @Days INT , @IncludeChildren BIT ) RETURNS @TableOfValues TABLE (
CREATE FUNCTION [dbo].[Web_GetDailyLoadListUDF]
(
@CustomerID INT
, @StartDate DATETIME
, @Days INT
, @IncludeChildren BIT
)
RETURNS @TableOfValues TABLE
(
RowID SMALLINT IDENTITY(1,1)
, DailyLoadCount INT
, DailyLoadDate VARCHAR(6)
, FullDate DATETIME
)
AS
BEGIN
DECLARE @MaxDate DATETIME;
SET @MaxDate = DATEADD(dd, @Days * -1.7, DATEDIFF(dd, 0, @StartDate));
WITH DateCTE AS
(
SELECT DATEADD(dd, 0, DATEDIFF(dd, 0, @StartDate)) AS DateValue
UNION ALL
SELECT DATEADD(DAY, -1, DateValue)
FROM DateCTE
WHERE DATEADD(DAY, -1, DateValue) > @MaxDate
)
INSERT INTO @TableOfValues
SELECT * FROM
(
SELECT TOP (@Days)
(
SELECT COUNT(*)
FROM dbo.[Load] l WITH (NOLOCK)
JOIN dbo.LoadCustomer lc WITH (NOLOCK)
ON lc.LoadID = l.ID
JOIN dbo.Customer c WITH (NOLOCK)
ON c.ID = lc.CustomerID
WHERE DATEADD(dd, 0, DATEDIFF(dd, 0, l.LoadDate)) = dct.DateValue
AND l.StateType = 1
AND lc.Main = 1
AND (c.ID = @CustomerID OR (@IncludeChildren = 1 AND c.ParentCustomerID = @CustomerID))
) AS DailyLoadCount
, CONVERT(VARCHAR(6), dct.DateValue, 107) AS DailyLoadDate
, dct.DateValue
FROM DateCTE dct
WHERE
DATEPART(DW, dct.DateValue) NOT IN (1, 7)
AND dct.DateValue NOT IN
(
SELECT HolidayDate FROM Holiday
)
ORDER BY dct.DateValue DESC
) AS S
ORDER BY s.DateValue ASC
RETURN
END
此SQL应该检索的是过去@天(无周末/假日)内每天的加载数
我基本上只是需要一些帮助来优化它,这样它就不会运行得太慢。(每个客户最多需要20秒,这将被调用数千次)。您的主要问题就在这里
WHERE DATEADD(dd, 0, DATEDIFF(dd, 0, l.LoadDate)) = dct.DateValue
应该是
WHERE l.LoadDate >= dct.DateValue
AND l.LoadDate < dct.DateValue +1
然后,在文本模式下运行以下命令,即按Ctrl-T然后按Ctrl-e
DECLARE
@CustomerID INT
, @StartDate DATETIME
, @Days INT
, @IncludeChildren BIT
SELECT
@CustomerID = 1
, @StartDate = '20110201'
, @Days = 10
, @IncludeChildren = 1
DECLARE @TableOfValues TABLE
(
RowID SMALLINT IDENTITY(1,1)
, DailyLoadCount INT
, DailyLoadDate VARCHAR(6)
, FullDate DATETIME
)
DECLARE @MaxDate DATETIME;
SET @MaxDate = DATEADD(dd, @Days * -1.7, DATEDIFF(dd, 0, @StartDate));
WITH DateCTE AS
(
SELECT DATEADD(dd, 0, DATEDIFF(dd, 0, @StartDate)) AS DateValue
UNION ALL
SELECT DATEADD(DAY, -1, DateValue)
FROM DateCTE
WHERE DATEADD(DAY, -1, DateValue) > @MaxDate
)
INSERT INTO @TableOfValues
SELECT * FROM
(
SELECT TOP (@Days)
(
SELECT COUNT(*)
FROM dbo.[Load] l WITH (NOLOCK)
JOIN dbo.LoadCustomer lc WITH (NOLOCK)
ON lc.LoadID = l.ID
JOIN dbo.Customer c WITH (NOLOCK)
ON c.ID = lc.CustomerID
WHERE DATEADD(dd, 0, DATEDIFF(dd, 0, l.LoadDate)) = dct.DateValue
AND l.StateType = 1
AND lc.Main = 1
AND (c.ID = @CustomerID OR (@IncludeChildren = 1 AND c.ParentCustomerID = @CustomerID))
) AS DailyLoadCount
, CONVERT(VARCHAR(6), dct.DateValue, 107) AS DailyLoadDate
, dct.DateValue
FROM DateCTE dct
WHERE
DATEPART(DW, dct.DateValue) NOT IN (1, 7)
AND dct.DateValue NOT IN
(
SELECT HolidayDate FROM Holiday
)
ORDER BY dct.DateValue DESC
) AS S
ORDER BY s.DateValue ASC
SELECT * FROM @TableOfValues
将计划编辑到您的问题中您的主要问题就在这里
WHERE DATEADD(dd, 0, DATEDIFF(dd, 0, l.LoadDate)) = dct.DateValue
应该是
WHERE l.LoadDate >= dct.DateValue
AND l.LoadDate < dct.DateValue +1
然后,在文本模式下运行以下命令,即按Ctrl-T然后按Ctrl-e
DECLARE
@CustomerID INT
, @StartDate DATETIME
, @Days INT
, @IncludeChildren BIT
SELECT
@CustomerID = 1
, @StartDate = '20110201'
, @Days = 10
, @IncludeChildren = 1
DECLARE @TableOfValues TABLE
(
RowID SMALLINT IDENTITY(1,1)
, DailyLoadCount INT
, DailyLoadDate VARCHAR(6)
, FullDate DATETIME
)
DECLARE @MaxDate DATETIME;
SET @MaxDate = DATEADD(dd, @Days * -1.7, DATEDIFF(dd, 0, @StartDate));
WITH DateCTE AS
(
SELECT DATEADD(dd, 0, DATEDIFF(dd, 0, @StartDate)) AS DateValue
UNION ALL
SELECT DATEADD(DAY, -1, DateValue)
FROM DateCTE
WHERE DATEADD(DAY, -1, DateValue) > @MaxDate
)
INSERT INTO @TableOfValues
SELECT * FROM
(
SELECT TOP (@Days)
(
SELECT COUNT(*)
FROM dbo.[Load] l WITH (NOLOCK)
JOIN dbo.LoadCustomer lc WITH (NOLOCK)
ON lc.LoadID = l.ID
JOIN dbo.Customer c WITH (NOLOCK)
ON c.ID = lc.CustomerID
WHERE DATEADD(dd, 0, DATEDIFF(dd, 0, l.LoadDate)) = dct.DateValue
AND l.StateType = 1
AND lc.Main = 1
AND (c.ID = @CustomerID OR (@IncludeChildren = 1 AND c.ParentCustomerID = @CustomerID))
) AS DailyLoadCount
, CONVERT(VARCHAR(6), dct.DateValue, 107) AS DailyLoadDate
, dct.DateValue
FROM DateCTE dct
WHERE
DATEPART(DW, dct.DateValue) NOT IN (1, 7)
AND dct.DateValue NOT IN
(
SELECT HolidayDate FROM Holiday
)
ORDER BY dct.DateValue DESC
) AS S
ORDER BY s.DateValue ASC
SELECT * FROM @TableOfValues
将计划编辑到您的问题中您应该使用内联自定义项(现在您实际使用的是临时表) 看
或者将其转换为视图。您应该使用内联UDF(现在您实际使用的是临时表) 看
或者将其转换为视图。相关子查询逐行运行,不要使用它们。改为使用联接或派生表的联接。您还需要确保任何where子句都可以利用索引。搜索saragble查询,查看哪些内容不能使用索引,以及如何使其使用索引。相关子查询逐行运行,不要使用它们。改为使用联接或派生表的联接。您还需要确保任何where子句都可以利用索引。搜索saragble查询,查看哪些内容不能使用索引,以及可以采取哪些措施使其使用索引。我不认为问题出在递归表上(它应该非常快),但对于
SELECT上的子查询
我不认为问题出在递归表上(它应该非常快),但是在选择的子查询中,在这里包含查询计划的最佳方式是什么?我试过你的建议,但不是没有改进,就是看起来很小。@Manny-请看回答中的步骤。我对这个评论有点草率。我没有注意到你写的关于综合指数的内容。在加入这个指数后,我看到了巨大的进步。我以为我自己已经加上了,但看起来我错过了。谢谢在这里包含查询计划的最佳方式是什么?我试过你的建议,但不是没有改进,就是看起来很小。@Manny-请看回答中的步骤。我对这个评论有点草率。我没有注意到你写的关于综合指数的内容。在加入这个指数后,我看到了巨大的进步。我以为我自己已经加上了,但看起来我错过了。谢谢我说没有临时表是因为大多数人试图通过用日期填充临时表,然后对其进行外部联接来解决这个问题。我说没有临时表是因为大多数人试图用日期填充临时表,然后对其进行外部联接来解决这个问题,等等。