Запрос T-SQL для поиска пропущенных идентификаторов для диапазона дат - PullRequest
1 голос
/ 23 февраля 2010

С учетом этих таблиц

table Channel
--------------
ChannelID int IDENTITY
<other irrelevant stuff>

table Program
--------------
ProgramID   int IDENTITY
ChannelID   int
AiringDate  datetime
<other irrelevant stuff>

и этот запрос

SELECT   C.ChannelID, t.AiringDate
FROM 
Channel C
LEFT JOIN ( 
    SELECT distinct ChannelID
    FROM   Program 
    WHERE AiringDate = '2010-01-16'
) p 
        ON p.ChannelID=C.ChannelID 
CROSS JOIN (
    SELECT AiringDate = '2010-01-16'
) t
WHERE   C.ChannelID IN (1, 2, 74, 15, 906)  /* the Channel table contains more channels than we are interested in */
    AND p.ChannelID IS NULL

, что дает

ChannelID | AiringDate
----------|-----------
    2     | 2010-01-16
   906    | 2010-01-16

как я могу изменить его так, чтобы он принимал диапазон дат, чтобы результат был примерно таким:

ChannelID | AiringDate
----------|-----------
    2     | 2010-01-16
   906    | 2010-01-16
    2     | 2010-01-17
   906    | 2010-01-17

если на этих 2 каналах не было программ, транслируемых в течение двух дней

Это не возвращает строк

SELECT   C.ChannelID, t.AiringDate
FROM 
Channel C
LEFT JOIN ( 
    SELECT distinct ChannelID, AiringDate
    FROM   Program 
    WHERE AiringDate between '2010-01-16' and '2010-01-17'
) p 
        ON p.ChannelID=C.ChannelID 
CROSS JOIN (
    SELECT AiringDate = '2010-01-16'
    union
    SELECT AiringDate = '2010-01-17'
) t
WHERE   C.ChannelID IN (1, 2, 74, 15, 906) 
    AND p.ChannelID IS NULL

Это CROSS JOIN немного уродливо, и было бы неплохо полностью от него избавиться. Обратите внимание, что первый пример запроса немного запутан; Для отдельных дат у меня есть более простая, которая выводит только недостающие идентификаторы каналов:

SELECT   C.ChannelID
FROM 
Channel C
LEFT JOIN ( 
    SELECT distinct ChannelID
    FROM   Program 
    WHERE  AiringDate = '2010-01-16'
) p 
        ON p.ChannelID=C.ChannelID 
WHERE   C.ChannelID IN (1, 2, 74, 15, 906) 
    AND p.ChannelID IS NULL

1 Ответ

2 голосов
/ 23 февраля 2010

Если я правильно понимаю, это должно получить то, что вы просили.

  • Объедините все каналы с каждой датой AiringDate
  • Выберите все каналы с AiringDates между нужными вам датами.
  • LEFT JOIN для удаления всех каналов, уже имеющих AiringDates

Оператор SQL

DECLARE @Channel TABLE (ChannelID INTEGER PRIMARY KEY)
DECLARE @Program TABLE (ProgramID INTEGER PRIMARY KEY, ChannelID INTEGER, AiringDate DATETIME)

INSERT INTO @Channel VALUES (1) -- Aired on 16 & 17
INSERT INTO @Channel VALUES (2) -- Aired on 16
INSERT INTO @Channel VALUES (3) -- Not Aired

INSERT INTO @Program VALUES (1, 1, '01-16-2010')
INSERT INTO @Program VALUES (2, 1, '01-17-2010')
INSERT INTO @Program VALUES (3, 2, '01-16-2010')


SELECT  C.*
FROM    (
          SELECT  C.ChannelID 
                  , p.AiringDate 
          FROM    @Channel C        
                  CROSS JOIN ( 
                    SELECT  DISTINCT AiringDate 
                    FROM    @Program 
                    WHERE   AiringDate BETWEEN '01-16-2010' AND '01-17-2010'
                  ) p 
        ) c
        LEFT OUTER JOIN (
          SELECT  C.ChannelID, p.AiringDate
          FROM    @Channel C
                  INNER JOIN @Program p ON p.ChannelID = C.ChannelID
          WHERE   AiringDate BETWEEN '01-16-2010' AND '01-17-2010' 
        ) p ON p.ChannelID = C.ChannelID AND p.AiringDate = C.AiringDate
WHERE   p.ChannelID IS NULL AND p.AiringDate IS NULL
...