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); 

它给出了一个重复搜索但没有排序的计划