DateCreated 问题
Issue with DateCreated
我有一个 table dbo.participation
:
ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED,
User VARCHAR(MAX) NOT NULL,
ParticipationLevel TINYINT NOT NULL,
Selector VARCHAR(MAX) NOT NULL,
DateCreated DATETIME NOT NULL
我创建了下面的代码,但不幸的是它显示 @DateStart
和 @DateStop
的性能不佳
SELECT
dateadd(month, datediff(month, 0, DateCreated), 0) AS MDate
,COUNT(CASE WHEN ParticipationLevel >= 10 THEN Selector ELSE NULL END) AS ParticipationLevel1
,COUNT(CASE WHEN ParticipationLevel >= 30 THEN Selector ELSE NULL END) AS ParticipationLevel2
FROM
Participation
WHERE
(@DateStart IS NULL OR (@DateStart IS NOT NULL
AND DateCreated >= @DateStart))
AND (@DateEnd IS NULL OR (@DateEnd IS NOT NULL
AND DateCreate < @DateEnd))
GROUP BY
Dateadd(month, datediff(month, 0, DateCreate), 0)
您是否有任何想法可以改进我的代码或者如何修改 table 以提高性能?
您的 WHERE 子句
中不需要以下两项检查
@DateStart 不为空并且
@DateEnd 不为空且
SELECT dateadd(month, datediff(month, 0, DateCreated), 0) AS MDate
,COUNT(CASE WHEN ParticipationLevel >= 10 THEN Tracking ELSE NULL END) AS ParticipationLevel1
,COUNT(CASE WHEN ParticipationLevel >= 30 THEN Tracking ELSE NULL END) AS ParticipationLevel2
FROM Participation
WHERE (@DateStart IS NULL OR DateCreated >= @DateStart) AND (@DateEnd IS NULL OR DateCreate < @DateEnd)
GROUP BY Dateadd(month, datediff(month, 0, DateCreate), 0)
您需要一个索引如下
CREATE INDEX ix
ON dbo.Participation(DateCreated)
INCLUDE (ParticipationLevel);
并且您应该重写查询以删除 OR
并避免不必要地引用定义为 NOT NULL
.
的列
(请注意,一个简单的 COUNT(Selector)
不会查找该值,因为 SQL 服务器认为它不能为 NULL,但用表达式包装会破坏此逻辑)
SELECT DATEADD(month, DATEDIFF(month, 0, DateCreated), 0) AS MDate,
COUNT(CASE
WHEN ParticipationLevel >= 10 THEN 1
END) AS ParticipationLevel1,
COUNT(CASE
WHEN ParticipationLevel >= 30 THEN 1
END) AS ParticipationLevel2
FROM Participation
WHERE DateCreated >= ISNULL(@DateStart, '17530101')
AND DateCreated <= ISNULL(@DateEnd, '99991231')
GROUP BY DATEDIFF(month, 0, DateCreated)
这可以给一个计划如下
请注意,可以通过每月处理索引块(可能在递归 CTE 中)来摆脱排序,但这可能有点过头了。
代码可能类似于
/*Cheap to find out from the index*/
IF @DateStart IS NULL
SELECT @DateStart = MIN(DateCreated)
FROM dbo.Participation
IF @DateStart IS NULL
SELECT @DateEnd = MAX(DateCreated)
FROM dbo.Participation
/*Adjust to start of month*/
SELECT @DateStart = DATEADD(month, DATEDIFF(month, 0, @DateStart), 0),
@DateEnd = DATEADD(month, 1 + DATEDIFF(month, 0, @DateEnd), 0);
WITH Dates
AS (SELECT @DateStart AS MDate
UNION ALL
SELECT dateadd(MONTH, 1, MDate) AS MDate
FROM Dates
WHERE dateadd (MONTH, 1, MDate) <= @DateEnd)
SELECT D.MDate,
CA.ParticipationLevel1,
CA.ParticipationLevel2
FROM Dates D
CROSS APPLY (SELECT COUNT(CASE
WHEN ParticipationLevel >= 10
THEN 1
END) AS ParticipationLevel1,
COUNT(CASE
WHEN ParticipationLevel >= 30
THEN 1
END) AS ParticipationLevel2
FROM Participation P WITH (INDEX = ix)
WHERE P.DateCreated >= D.MDate
AND P.DateCreated < DATEADD(MONTH, 1, D.MDate)
GROUP BY () /* So no grouping row returned for empty months */
) CA(ParticipationLevel1, ParticipationLevel2)
OPTION (MAXRECURSION 0);
它给出了一个重复搜索但没有排序的计划
我有一个 table dbo.participation
:
ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED,
User VARCHAR(MAX) NOT NULL,
ParticipationLevel TINYINT NOT NULL,
Selector VARCHAR(MAX) NOT NULL,
DateCreated DATETIME NOT NULL
我创建了下面的代码,但不幸的是它显示 @DateStart
和 @DateStop
SELECT
dateadd(month, datediff(month, 0, DateCreated), 0) AS MDate
,COUNT(CASE WHEN ParticipationLevel >= 10 THEN Selector ELSE NULL END) AS ParticipationLevel1
,COUNT(CASE WHEN ParticipationLevel >= 30 THEN Selector ELSE NULL END) AS ParticipationLevel2
FROM
Participation
WHERE
(@DateStart IS NULL OR (@DateStart IS NOT NULL
AND DateCreated >= @DateStart))
AND (@DateEnd IS NULL OR (@DateEnd IS NOT NULL
AND DateCreate < @DateEnd))
GROUP BY
Dateadd(month, datediff(month, 0, DateCreate), 0)
您是否有任何想法可以改进我的代码或者如何修改 table 以提高性能?
您的 WHERE 子句
中不需要以下两项检查
@DateStart 不为空并且
@DateEnd 不为空且
SELECT dateadd(month, datediff(month, 0, DateCreated), 0) AS MDate
,COUNT(CASE WHEN ParticipationLevel >= 10 THEN Tracking ELSE NULL END) AS ParticipationLevel1
,COUNT(CASE WHEN ParticipationLevel >= 30 THEN Tracking ELSE NULL END) AS ParticipationLevel2
FROM Participation
WHERE (@DateStart IS NULL OR DateCreated >= @DateStart) AND (@DateEnd IS NULL OR DateCreate < @DateEnd)
GROUP BY Dateadd(month, datediff(month, 0, DateCreate), 0)
您需要一个索引如下
CREATE INDEX ix
ON dbo.Participation(DateCreated)
INCLUDE (ParticipationLevel);
并且您应该重写查询以删除 OR
并避免不必要地引用定义为 NOT NULL
.
(请注意,一个简单的 COUNT(Selector)
不会查找该值,因为 SQL 服务器认为它不能为 NULL,但用表达式包装会破坏此逻辑)
SELECT DATEADD(month, DATEDIFF(month, 0, DateCreated), 0) AS MDate,
COUNT(CASE
WHEN ParticipationLevel >= 10 THEN 1
END) AS ParticipationLevel1,
COUNT(CASE
WHEN ParticipationLevel >= 30 THEN 1
END) AS ParticipationLevel2
FROM Participation
WHERE DateCreated >= ISNULL(@DateStart, '17530101')
AND DateCreated <= ISNULL(@DateEnd, '99991231')
GROUP BY DATEDIFF(month, 0, DateCreated)
这可以给一个计划如下
请注意,可以通过每月处理索引块(可能在递归 CTE 中)来摆脱排序,但这可能有点过头了。
代码可能类似于
/*Cheap to find out from the index*/
IF @DateStart IS NULL
SELECT @DateStart = MIN(DateCreated)
FROM dbo.Participation
IF @DateStart IS NULL
SELECT @DateEnd = MAX(DateCreated)
FROM dbo.Participation
/*Adjust to start of month*/
SELECT @DateStart = DATEADD(month, DATEDIFF(month, 0, @DateStart), 0),
@DateEnd = DATEADD(month, 1 + DATEDIFF(month, 0, @DateEnd), 0);
WITH Dates
AS (SELECT @DateStart AS MDate
UNION ALL
SELECT dateadd(MONTH, 1, MDate) AS MDate
FROM Dates
WHERE dateadd (MONTH, 1, MDate) <= @DateEnd)
SELECT D.MDate,
CA.ParticipationLevel1,
CA.ParticipationLevel2
FROM Dates D
CROSS APPLY (SELECT COUNT(CASE
WHEN ParticipationLevel >= 10
THEN 1
END) AS ParticipationLevel1,
COUNT(CASE
WHEN ParticipationLevel >= 30
THEN 1
END) AS ParticipationLevel2
FROM Participation P WITH (INDEX = ix)
WHERE P.DateCreated >= D.MDate
AND P.DateCreated < DATEADD(MONTH, 1, D.MDate)
GROUP BY () /* So no grouping row returned for empty months */
) CA(ParticipationLevel1, ParticipationLevel2)
OPTION (MAXRECURSION 0);
它给出了一个重复搜索但没有排序的计划