Как структурировать SQL-оператор - PullRequest
2 голосов
/ 22 сентября 2010

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

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

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

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

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

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

Ответы [ 4 ]

2 голосов
/ 22 сентября 2010

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

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

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

2 голосов
/ 22 сентября 2010

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

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

Замените x и y на дату, после которой вы.

0 голосов
/ 22 сентября 2010

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

Оператор 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 голосов
/ 22 сентября 2010

Я не проверял это, но посмотрите, поможет ли это:

select ID, TaskDate as A from Tasks,TasksDone
where TaskID not in (select TaskID from TasksDone where TaskDate = A)
GROUP BY TaskDate
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...