2010-09-22 58 views
2

我有一个包含任务列表的表;如何构建SQL语句

表名:任务。字段:(ID Int,描述nvarchar)

任务每天完成并记录在如下表格中;

TableName TasksDone。字段:(的TaskID Int,TaskDate日期时间)

我需要有一个查询运行日期范围,并显示未完成的任务(不存在于TasksDone表)的范围内的每个日期。

我希望这是有道理的... 感谢您的任何帮助。

+0

有没有内置的功能在SQL Server中得到的范围内,或类似的所有日期。您需要使用数字表格或日历表格进行构建。 – 2010-09-22 06:43:52

回答

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 

前两个的CTE建一个小的数字表,第三CTE构建所需范围内的一组TaskID的和日期。最后的选择与TasksDone表相匹配,然后放弃找到匹配的那些行。如果TasksDone.TaskDate是一个普通的日期(没有时间组件),@StartDate也没有时间组件,那么你可以抛弃DATEDIFF并使用td1.TaskDate = td2.TaskDate。

如果你需要一个大的范围(上面可覆盖〜3年),我建议建立一个适当的数字表或日历表

+0

太棒了,这看起来棒极了!给我几分钟试试吧! – rickj 2010-09-22 06:46:18

+0

@rickj - 请确保您使用的是当前版本 - 当我第一次发布它时,我有错误的方式向DATEADD提供参数。 – 2010-09-22 06:49:11

+0

绝对太棒了!作品一种享受。也非常感谢你的解释,帮助我理解到底发生了什么。 – rickj 2010-09-22 07:09:13

2

这是相当简单的,如果我理解正确的问题:

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

更换xy与你以后的日期。

+0

你好,谢谢。问题是我需要为范围内的每个日期显示缺失的任务,所有任务必须每天完成。我需要显示在该范围内每天缺少的任务。合理? – rickj 2010-09-22 05:25:50

+0

@rickj,你真的试过查询吗?它的确如此... – 2010-09-22 06:30:33

+0

@Lieven,是的,我做了,没有它不给我以后的。它只显示未在整个范围内完成的任务,而不是范围内的每个日期。任务表中的所有任务都需要每天完成。我需要显示该范围内每天都缺少的所有任务。 – rickj 2010-09-22 06:36:44

0

我没有测试了这一点,但看看这有助于:

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

如果我理解正确的,下面的语句应该得到你,没任务”在整个范围内每天都得到执行。

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 2010-09-22 07:11:35

+0

没问题。看起来有点亮的派对,但如果可以的话,让我们知道结果。 – 2010-09-22 07:18:00

+0

@rickj,我搞乱了'HAVING'条款。答案已经过修改以解决错误。 – 2010-09-22 07:25:24