0

我有一张桌子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)

您是否碰巧有任何想法如何改进我的代码,或者如何修改表格以提高性能?

4

2 回答 2

1

您需要以下几行的索引

CREATE INDEX ix
  ON dbo.Participation(DateCreated)
  INCLUDE (ParticipationLevel);

并且您应该重写查询以摆脱OR并避免对定义为的列进行不必要的引用NOT NULL

(注意一个简单的COUNT(Selector)不会查找该值,因为 SQL Server 识别它不能为 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); 

这给出了一个反复寻找但没有排序的计划

在此处输入图像描述

于 2015-03-25T19:38:35.157 回答
0

WHERE子句中不需要以下两项检查
@DateStart IS NOT NULL AND
@DateEnd IS NOT NULL AND

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)
于 2015-03-25T19:03:39.337 回答