如何构造SQL语句

时间:2010-09-22 05:14:43

标签: sql-server sql-server-2005

我有一个包含任务列表的表格;

TableName:任务。字段:(ID Int,Description nvarchar)

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

TableName TasksDone。字段:(TaskID Int,TaskDate DateTime)

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

我希望这是有道理的...... 谢谢你的帮助。

4 个答案:

答案 0 :(得分:2)

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

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

xy替换为您之后的日期。

答案 1 :(得分: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和Dates。最终选择将这些与TasksDone表相匹配,然后丢弃找到匹配项的那些行。如果TasksDone.TaskDate是一个普通日期(没有时间组件)而且@StartDate也没有时间组件,那么你可以抛弃DATEDIFF并使用td1.TaskDate = td2.TaskDate。

如果你需要大范围(以上可以覆盖〜3年),我建议建立一个合适的号码表或日历表

答案 2 :(得分:0)

我没有对此进行测试,但看看这是否有帮助:

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

答案 3 :(得分: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