Интересный SQL Join по датам между датами - PullRequest
17 голосов
/ 18 августа 2011

Прежде всего, спасибо всем, кто помогает мне решить эту проблему. Я использую SQL 2005, но могу использовать 2008, если в 05 нет решения.

У меня есть строки данных, которые выглядят так:

select * from mySPtable

| myPK | Area | RequestType |  StartDate  |  EndDate  |
   1      SB        ADD        8/14/2011    8/18/2011
   2      NB        RMV        8/16/2011    8/16/2011

Итак, я хочу подсчитать общее количество запросов по каждой области за день. Результаты должны быть:

|  myDate  | RequestType |  Area  | myCount |
  8/14/2011      ADD         SB        1
  8/15/2011      ADD         SB        1
  8/16/2011      ADD         SB        1
  8/16/2011      RMV         NB        1
  8/17/2011      ADD         SB        1
  8/18/2011      ADD         SB        1

Как мне это сделать? Я зашла в тупик, и никакое прибегание к помощи не помогло.

Ответы [ 3 ]

10 голосов
/ 18 августа 2011

Вам понадобится либо таблица календаря, либо вы можете сгенерировать ее с помощью CTE. Как только вы это сделаете, остальная часть запроса должна быть довольно тривиальной. Подход CTE может быть немного сложным из-за проблем рекурсии и невозможности использования агрегатов, поэтому ниже я использовал табличную переменную. Вы также можете сделать эту таблицу постоянной, которую храните в своей базе данных.

SET NOCOUNT ON

DECLARE @Calendar TABLE (my_date DATETIME NOT NULL)
DECLARE @date DATETIME, @max_date DATETIME

SELECT @date = MIN(StartDate), @max_date = MAX(EndDate) FROM My_Table

WHILE (@date <= @max_date)
BEGIN
    INSERT INTO @Calendar (my_date) VALUES (@date)
    SELECT @date = DATEADD(dy, 1, @date)
END

SELECT
    C.myDate,
    M.RequestType,
    M.Area,
    COUNT(*) AS myCount
FROM
    @Calendar C
INNER JOIN My_Table M ON
    M.StartDate <= C.myDate AND
    M.EndDate >= C.myDate
GROUP BY
    C.myDate,
    M.RequestType,
    M.Area
ORDER BY
    C.myDate,
    M.RequestType,
    M.Area

В зависимости от того, насколько велик ваш потенциальный диапазон дат, заполнение табличной переменной может занять некоторое время. Например, если диапазон охватывает десятилетие или два.

3 голосов
/ 18 августа 2011

Звучит так, как будто вам нужен файл 'Календарь' . Особенно в рамках более крупной организации бизнеса, это станет чрезвычайно полезным.

После создания календаря вы можете получить таблицу со следующими данными:

SELECT a.isoDate, b.RequestType, b.Area, count(*)
FROM calendar as a
JOIN mySPTable as b
ON a.isoDate between b.StartDate and b.EndDate
WHERE a.isoDate >= [input_start_date] 
      AND a.isoDate < [input_end_date]
GROUP BY a.isoDate, b.RequestType, b.Area

Это создаст строку для каждой даты в файле календаря, которая находится между начальной и конечной датами хотя бы для одной строки mySPTable.

В качестве примечания также можно сгенерировать диапазон дат с помощью рекурсивного CTE, но особенно в долгосрочной перспективе я бы рекомендовал создать и использовать файл календаря. Быстрый CTE:

WITH DateRange (thisDate) as (SELECT [input_start_date]
                              UNION ALL
                              SELECT DATEADD(dy, 1, thisDate)
                              FROM DateRange
                              WHERE thisDate < [input_end_date])
2 голосов
/ 18 августа 2011

Вы можете сделать это с помощью таблицы чисел (начиная с 0). Здесь я использую master..spt_values ​​вместо этого. SQL, Вспомогательная таблица чисел

select dateadd(day, N.Number, M.StartDate) as myDate,
       RequestType,
       Area, 
       count(*) as myCount
from mySPtable as M
  inner join master..spt_values as N
    on N.Number <= datediff(day, M.StartDate, M.EndDate)
where N.type = 'P'
group by dateadd(day, N.Number, M.StartDate),
         RequestType,
         Area
order by dateadd(day, N.Number, M.StartDate)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...