DateCreated问题

时间:2015-03-25 18:49:48

标签: sql-server date stored-procedures datecreated

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

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

2 个答案:

答案 0 :(得分: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) 

这可以给出一个寻求下面的计划

enter image description here

请注意,可以通过在当时处理索引的块(可能在递归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); 

这给出了一个反复寻求并且没有排序的计划

enter image description here

答案 1 :(得分:0)

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

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)