2015-03-25 2 views
0

У меня есть таблица dbo.participation:Проблема с DateCreated

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) 

ли вы, случается, есть какие-либо идеи, как улучшить мой код или, альтернативно, как изменить таблицу для повышения производительности?

+1

Вы могли бы упростить вещи немного, установив DateStart в мин дату, если null и DateEnd до max-date, прежде чем использовать их в запросе ... исключая два предложения OR, которые всегда плохо относятся к производительности. Также убедитесь, что столбец DateCreated имеет соответствующий индекс, а PartitionLevel - в качестве столбца include. Я не вижу определения для «Отслеживания», хотя ... – pmbAustin

+1

У вас есть какие-либо индексы на вашей таблице ?. Вы можете использовать индекс с ключом в 'DateCreated', и вы можете включить' ParticipationLevel'. Вам действительно нужно считать 'Tracking' ?, если этот столбец не может иметь' NULL', вы могли бы просто использовать 'COUNT (1)' вместо – Lamak

+0

Оба хороших комментария выше. Еще одна информация? Сколько строк в таблице? Сколько месяцев он содержит? –

ответ

1

Вам нужен индекс по следующим направлениям

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

И вы должны переписать запрос, чтобы избавиться от OR и избежать ненужного рефери nce в столбец, определяемый как 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

Обратите внимание, что можно было бы избавиться от сортировки по обработке ломти индекса в месяц в то время (возможно, в рекурсивном КТР), но это может быть слишком много.

Код для этого может выглядеть

/*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

0

Ниже двух проверок не требуется в вашем ГДЕ пункт
@DateStart НЕ 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) 
+0

Это очень лишнее условие. Это не должно влиять на производительность (хотя это ненужные условия) – Lamak

+0

Извините, я увидел его в OP на втором взгляде и снял свой комментарий. –

Смежные вопросы