Как я могу оптимизировать SQL-запрос, который выполняет подсчет, вложенный в предложение group-by? - PullRequest
1 голос
/ 17 февраля 2011

У меня есть приложение для построения диаграмм, которое динамически генерирует запросы SQL Server для вычисления значений для каждой серии в данной диаграмме. Обычно это работает довольно хорошо, но я столкнулся с конкретной ситуацией, в которой сгенерированный запрос очень медленный. Запрос выглядит так:

SELECT 
  [dateExpr] AS domainValue,
  (SELECT COUNT(*) FROM table1 WHERE [dateExpr]=[dateExpr(maintable)] AND column2='A') AS series1

FROM table1 maintable
GROUP BY [dateExpr]
ORDER BY domainValue

Я сократил [dateExpr], потому что это комбинация функций CAST и DATEPART, которые преобразуют поле datetime в строку в форме 'yyyy-MM-dd', так что я могу легко группировать по всем значениям в календарный день , Приведенный выше запрос возвращает оба значения yyyy-MM-dd в качестве меток для оси x диаграммы и значения из ряда данных "series1" для отображения на диаграмме. Предполагается, что в серии данных учитывается количество записей, приходящихся на этот календарный день, которые также содержат определенное значение в [column2]. Выражение «[dateExpr] = [dateExpr (maintable)]» выглядит так:

CAST(DATEPART(YEAR,dateCol) AS VARCHAR)+'-'+CAST(DATEPART(MONTH,dateCol) AS VARCHAR) = 
CAST(DATEPART(YEAR,maintable.dateCol) AS VARCHAR)+'-'+CAST(DATEPART(MONTH,maintable.dateCol) AS VARCHAR)

с дополнительным сроком на день (опущено выше для экономии места). Это источник медлительности запроса, но я не знаю, как переписать запрос, чтобы он возвращал тот же результат более эффективно. У меня есть полный контроль над генерацией запроса, поэтому, если бы я мог найти более эффективный SQL, который дал бы те же результаты, я мог бы соответствующим образом изменить генератор запросов. Любые указатели будут с благодарностью.

Ответы [ 3 ]

2 голосов
/ 17 февраля 2011

Я не проверял, но я думаю, что это может быть сделано:

SELECT 
  [dateExpr] AS domainValue,
  SUM (CASE WHEN  column2='A' THEN 1 ELSE 0 END) AS series1

FROM table1 maintable
GROUP BY [dateExpr]
ORDER BY domainValue
1 голос
/ 17 февраля 2011
With Calendar As
    (
    Select DateAdd(d, DateDiff(d, 0, Min( dateCol ) ), 0) As [date]
    From Table1
    Union All
    Select DateAdd(d, 1, [date])
    From Calendar
    Where [date] <= (
                    Select Max( DateAdd(d, DateDiff(d, 0, dateCol) + 1, 0) )
                    From Table1
                    )
    )
Select C.date, Count(Table1.PK) As Total
From Calendar As C
        Left Join Table1
            On Table1.dateCol >= C.date
                And Table1.dateCol < DateAdd(d, 1, C.date )
                And Table1.column2 = 'A'
Group By C.date
Option (Maxrecursion 0);

Вместо того чтобы пытаться форсировать формат отображения в SQL, вы должны сделать это в своем генераторе отчетов или диаграмм. Однако то, что вы можете сделать в SQL, это убрать часть времени из значений даты и времени, как я делал в своем решении.

1 голос
/ 17 февраля 2011

Самый быстрый способ сделать это - использовать таблицы календаря. Создайте таблицу SQL с записью для каждого месяца для следующего, кто знает, сколько лет. Затем выберите из этой календарной таблицы, объединяя записи из таблицы1, в которых есть даты между начальной и конечной датами месяца. Затем, если ваш кластерный индекс находится в элементе dateCol в таблице 1, запрос будет выполняться очень быстро.

РЕДАКТИРОВАТЬ: Пример запроса. Предполагается, что таблица месяцев существует с двумя столбцами: StartDate и EndDate, где EndDate - полночь первого дня следующего месяца. Кластерный индекс в таблице месяцев должен быть на StartDate

SELECT
    months.StartDate,
    COUNT(*) AS [Count]
FROM months
INNER JOIN table1
    ON table1.dateCol >= months.StartDate AND table1.dateCol < months.EndDate
GROUP BY months.StartDate;
...