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

(SQL Server は値を NULL にすることはできないと認識しているため、単純なCOUNT(Selector)値は検索されませんが、式でラップするとこのロジックが無効になることに注意してください)

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) 

これにより、以下のようにシークを含む計画が得られます

ここに画像の説明を入力

インデックスのチャンクを 1 か月に 1 回 (おそらく再帰 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 に答える