2010-09-22 4 views
2

У меня есть таблица, содержащая список задач;Как структурировать инструкцию SQL

TableName: Задачи. Поля: (ID Int, Описание nvarchar)

Задачи выполняются ежедневно и регистрируются в таблице, как показано ниже;

TableName TasksDone. Поля: (TaskID Int, TaskDate DateTime)

Мне нужен запрос, который выполняется для диапазона дат и показывает задачи, которые НЕ выполнялись (не существует в таблице TasksDone) для каждой даты в диапазоне.

Я надеюсь, что это имеет смысл ... Спасибо за любую помощь.

+0

Там нет встроенных функций в SQL Server, чтобы получить все даты в диапазоне, или аналогичный. Вам нужно построить его, используя таблицу чисел или таблицу календаря. –

ответ

2

Вам понадобится номер или календарную таблицу, чтобы сделать вещи легко, или мы можем моделировать один, если диапазон мал. Является ли TaskDate простой датой или же имеет компонент времени?

Основного плана атаки:

declare @StartDate datetime 
declare @EndDate datetime 

/* Set @StartDate and @EndDate to represent the range */ 

with Digits as (
    select 0 as d union all select 1 union all select 2 union all select 3 union all select 4 union all 
    select 5 union all select 6 union all select 7 union all select 8 union all select 9 
), Numbers as (
    select (D1.d * 100) + (D2.d * 10) + D3.d as n 
    from Digits D1,Digits D2,Digits D3 
), TaskDates as (
    select 
     t.TaskID, 
     DATEADD(day,n.n,@StartDate) as TaskDate 
    from 
     Tasks t 
      inner join 
     Numbers n 
      on 
       DATEADD(day,n.n,@StartDate) <= @EndDate 
) 
select 
    * 
from 
    TaskDates td1 
     left join 
    TasksDone td2 
     on 
      td1.TaskID = td2.TaskID and 
      DATEDIFF(day,td1.TaskDate,td2.TaskDate) = 0 
where 
    td2.TaskID is null 

Первых два КТРА построить небольшую таблицу чисел, третий КТР создает набор TaskIDs и даты в пределах требуемого диапазона. Окончательный выбор соответствует тезу против таблицы TasksDone, а затем удаляет те строки, где найдено совпадение. Если TasksDone.TaskDate является простой датой (без компонента времени) и @StartDate также не имеет компонента времени, тогда вы можете отключить DATEDIFF и просто использовать td1.TaskDate = td2.TaskDate.

Если вам нужен большой диапазон (выше может покрыть ~ 3-х лет), я бы предложил строить правильную таблицу чисел или календарную таблицу

+0

Удивительный, это отлично выглядит! Дайте мне несколько минут, чтобы попробовать! – rickj

+0

@rickj - убедитесь, что вы используете текущую версию - у меня были аргументы DATEADD неправильным способом, когда я впервые разместил его. –

+0

Абсолютно фантастический! Работает. Спасибо также за великое объяснение, помогает мне точно понять, что происходит. – rickj

2

Это довольно прямо вперед, если я правильно понять проблему:

SELECT * 
FROM Tasks 
WHERE ID NOT IN (SELECT TaskID FROM TasksDone WHERE TaskDate BETWEEN x AND y) 

Заменить x и y с датой вы после этого.

+0

Здравствуйте, Спасибо. Уловка - мне нужно показать недостающие задачи для каждой даты в диапазоне, все задачи должны выполняться каждый день. Мне нужно показать задачи, отсутствующие на каждый день в диапазоне. Имеют смысл? – rickj

+0

@ rickj, вы действительно попробовали запрос? Он делает именно это ... –

+0

@ Ливен, да, я сделал, и нет, это не дает мне то, что я за. Он показывает только задачи, которые не выполняются во всем диапазоне, а не для каждой даты в диапазоне.Все задачи в таблице Задачи должны быть заполнены КАЖДЫЙ день. Мне нужно показать все задачи, которые отсутствовали для КАЖДОГО дня в диапазоне. – rickj

0

Я не проверял это, но увидеть, если это помогает:

select ID, TaskDate as A from Tasks,TasksDone 
where TaskID not in (select TaskID from TasksDone where TaskDate = A) 
GROUP BY TaskDate 
0

Если я понимаю правильно, следующее заявление вы должны получить те задачи, которые Ждут» t выполняются каждый день во всем диапазоне.

SQL Заявление

SELECT t.* 
FROM @Tasks t 
     INNER JOIN (
      SELECT TaskID 
      FROM @TasksDone td 
      WHERE td.TaskDate BETWEEN @RangeStart AND @RangeEnd   
      GROUP BY 
        td.TaskID 
      HAVING COUNT(TaskID) < CAST(@RangeEnd - @RangeStart AS INTEGER)+1 
      UNION ALL 
      SELECT TaskID 
      FROM @TasksDone td 
      WHERE TaskID NOT IN (SELECT TaskID 
           FROM @TasksDone 
           WHERE TaskDate BETWEEN @RangeStart AND @RangeEnd) 
     ) td ON td.TaskID = t.ID  

тестовый скрипт

DECLARE @Tasks TABLE (
    ID INTEGER 
    , DESCRIPTION NVARCHAR(32) 
) 

DECLARE @TasksDone TABLE (
    TaskID INTEGER 
    , TaskDate DATETIME 
) 

DECLARE @RangeStart DATETIME 
DECLARE @RangeEnd DATETIME 

SET @RangeStart = GetDate() - 1 
SET @RangeEnd = GetDate() + 1 

INSERT INTO @Tasks 
      SELECT 1, 'Done Every Day in range.' 
UNION ALL SELECT 2, 'Done a few times in range.' 
UNION ALL SELECT 3 , 'Not done anytime in range.' 

INSERT INTO @TasksDone 
      SELECT 1, @RangeStart 
UNION ALL SELECT 1, GetDate() 
UNION ALL SELECT 1, @RangeEnd 
UNION ALL SELECT 2, GetDate() 
UNION ALL SELECT 3, GetDate() + 2 

SELECT t.* 
FROM @Tasks t 
     INNER JOIN (
      SELECT TaskID 
      FROM @TasksDone td 
      WHERE td.TaskDate BETWEEN @RangeStart AND @RangeEnd   
      GROUP BY 
        td.TaskID 
      HAVING COUNT(TaskID) < CAST(@RangeEnd - @RangeStart AS INTEGER)+1 
      UNION ALL 
      SELECT TaskID 
      FROM @TasksDone td 
      WHERE TaskID NOT IN (SELECT TaskID FROM @TasksDone WHERE TaskDate BETWEEN @RangeStart AND @RangeEnd) 
     ) td ON td.TaskID = t.ID  
+0

Спасибо, Ливен, только что заметил ваш ответ. Я тоже попробую! – rickj

+0

Нет проблем. Немного освещенный вечеринке, но, если можно, сообщите нам о результатах. –

+0

@rickj, я испортил предложение 'HAVING'. Ответ исправлен, чтобы исправить ошибку. –

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