【问题标题】:Check if given month+date is present in the data containing range of month+date检查包含月份+日期范围的数据中是否存在给定的月份+日期
【发布时间】:2011-08-16 01:39:31
【问题描述】:

这是我的查询:

DECLARE @MM INT -- Current month
DECLARE @DD INT -- Current date

SET @MM = 1 -- For testing, set it to January
SET @DD = 1 -- For testing, set it to 01

SELECT xxxID, xxxFK, StartMonth, StartDate, StopMonth, StopDate, NULL AS OKorNOT
FROM xxxTable
ORDER BY xxxFK

这是数据:

xxxID            xxxFK       StartMonth  StartDate   StopMonth   StopDate    OKorNOT     
---------------- ----------- ----------- ----------- ----------- ----------- ----------- 
8                2287        11          15          1           2           NULL
4                2290        2           1           2           21          NULL
2                2306        9           15          10          31          NULL
3                2306        1           3           1           20          NULL
9                2661        11          15          1           3           NULL
10               2661        5           5           5           31          NULL
5                3778        6           2           9           5           NULL
6                3778        1           1           3           31          NULL
7                3778        5           10          5           31          NULL
1                3778        12          10          12          31          NULL

我需要用 1/0 填充 OKorNot 列,具体取决于给定的月份日期是否介于 StartMonth-StartDate 和 StopMonth-StopDate 之间。顺便说一下,这是 SQL Server 2000。

编辑

这里要注意的是,数据中没有存储年份,月份日期可能从 11 月 15 日开始,到 1 月 15 日结束,所以在 12 月 31 日和 1 月 1 日这种情况下应该返回真。

【问题讨论】:

    标签: sql sql-server tsql datetime sql-server-2000


    【解决方案1】:

    仅使用整数运算和假想的 384 天日历

    由于您的日期是月份和日期的组合,因此我尝试为每个此类组合创建一个整数,该整数是唯一的并且还保留顺序。为了使计算尽可能简单,我们发明了一个新日历,其中所有月份正好有 32 天,我们的行为就好像我们的日期来自这个日历。然后要获得自 1 月 1 日以来已经过去了多少天,我们有以下公式:

    DaysPast = 32 * month + day

    (好吧,应该是32 * (month-1) + (day-1),但这样更简单,我们只想比较日期,而不是 1 月 1 日。而且每个日期的结果仍然是唯一的)。 em>

    因此,我们首先计算检查日期的DaysPast

    SET @CHECK = 32 * @MM + @DD

    然后,我们计算表中所有日期(包括开始日期和结束日期)的DaysPast

      ( SELECT *
             , (32 * StartMonth + StartDate) AS Start
             , (32 * StopMonth  + StopDate ) AS Stop
        FROM xxxTable
      ) AS temp
    

    那么,我们有两种情况。

    • 第一种情况,Start = (8-Feb)Stop = (23-Nov)

    那么,@CHECK BETWEEN Start AND Stop 的第一个条件为真,Start 和 Stop 之间的日期就可以了。

    第二个条件为 False,因此没有更多日期是可以的。

    • 第二种情况,当Start = (23-Nov)Stop = (8-Feb) 时。 :

    那么,@CHECK BETWEEN Start AND Stop 的第一个条件将为 false,因为 Start 大于 Stop,因此没有日期可以匹配此条件。

    第二个条件Stop < Start 为真,所以我们还要测试@CHECK 是否不是BETWEEN (9-Feb) AND (22-Nov)
    匹配(9-Feb) 之前或(22-Nov) 之后的日期。

    DECLARE @CHECK INT
    SET @CHECK = 32 * @MM + @DD
    
    SELECT *
         , CASE WHEN
               @CHECK BETWEEN Start AND Stop 
               OR ( Stop < Start 
                   AND @CHECK NOT BETWEEN Stop+1 AND Start-1
                  )
             THEN 1
             ELSE 0
           END
           AS OKorNOT
    FROM 
      ( SELECT *
             , (32 * StartMonth + StartDate) AS Start
             , (32 * StopMonth  + StopDate ) AS Stop
        FROM xxxTable
      ) AS temp
    ORDER BY xxxFK
    

    【讨论】:

    • 这个看起来更容易理解,但如果你能用一/两行解释如何处理环绕案例,我将不胜感激。
    • 太好了,我对您刚刚解释的第二种情况感到困惑。我回来工作了,所以我现在可以测试查询了。我会检查边界线情况,但这看起来很有希望。
    【解决方案2】:
    SELECT *
    FROM xxxTable
    WHERE (StartMonth < StopMonth OR StartMonth = StopMonth AND StartDate<=StopDate)
        AND (@MM > StartMonth OR @MM = StartMonth AND @DD >= StartDate)
        AND (@MM < StopMonth OR @MM = StopMonth AND @DD <= StopDate)
        OR (StartMonth > StopMonth OR StartMonth = StopMonth AND StartDate>StopDate)
            AND ((@MM > StartMonth OR @MM = StartMonth AND @DD >= StartDate)
                OR (@MM < StopMonth OR @MM = StopMonth AND @DD <= StopDate))
    

    【讨论】:

      【解决方案3】:

      如果您将日期存储为日期会更容易......

      无论如何,像这样。我没有测试过。你需要处理我已经完成的年份边界

      SELECT
          xxxID, xxxFK, StartMonth, StartDate, StopMonth, StopDate,
          CASE 
             WHEN
                FullStart <= FullStop AND 
                  DATEADD(month, @MM-1, DATEADD(day, @DD-1, 0)) BETWEEN FullStart AND FullStop
                            THEN 1
             WHEN
                FullStart > FullStop AND 
                  DATEADD(month, @MM-1, DATEADD(day, @DD-1, 0)) BETWEEN
                          FullStart AND DATEADD(year, 1, FullStop)
                            THEN 1  
             ELSE 0
          END AS OKOrNot
      FROM
          (
          SELECT
              xxxID, xxxFK, StartMonth, StartDate, StopMonth, StopDate,
              DATEADD(month, StartMonth-1, DATEADD(day, StartDate-1, 0)) AS FullStart,
              DATEADD(month, StopMonth-1, DATEADD(day, StopDate-1, 0)) AS FullStop
          FROM xxxTable
          ) foo
      ORDER BY xxxFK
      

      编辑:为所有值添加“-1”:如果我们已经是一月,请不要再添加一个月...

      【讨论】:

      • 我检查了,它对于 case #1 (8, 2287, 11, 15, 1, 2, 0) 失败,日期为 Jan/1... 应该返回 1/true/OK。
      • @Salman A:你试过玩这个吗?我面前没有 SQL Server
      • 修改FullStop列定义如下:DATEADD(year, CASE StopMonth * 100 + StopDay &lt; StartMonth * 100 + StartDay THEN 1 ELSE 0 END, DATEADD(month, StopMonth-1, DATEADD(day, StopDate-1, 0))) AS FullStop,即当前表达式应该嵌套到添加的DATEADD(year, ...)中。
      • @gbn:当FullStart &gt; FullStop 需要另外一个条件时。事实上,只有在年底的日期,在FullStart 之后才可以。年初日期,FullStop 之前的日期也需要检查。
      • @gbn:好像有,抱歉。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-20
      • 2023-03-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多