【问题标题】:sql - generate random date, exluding weekendssql - 生成随机日期,不包括周末
【发布时间】:2017-02-08 23:05:53
【问题描述】:

我想请您帮忙查询。我正在尝试获取 7.2.2016 - 14.2.2016 的随机日期列表,但我想排除 11. 和 12.2.2016,这将是周末。

这就是我所拥有的:

SELECT DATEADD(DAY, ABS(CHECKSUM(NEWID()) % 8), '2016-02-07')

我正在使用 SQL Server 2016。

并且日期应该随机分配给其他列:

enter image description here

SMS_send_day 应该是那些日期,不包括 11.2。和 12.2。

感谢您的建议!

【问题讨论】:

  • 您的意思是像 02/07/2016,02/08/2016 到 02/12/2016 之类的列表,而不是 11 和 12?总共 6 行?
  • 是的,我做到了,它应该随机分配给我上面提到的其余列。

标签: sql-server sql-server-2008 sql-server-2012


【解决方案1】:

不确定这是否有帮助,但这会为您提供一个日期,不包括您要求的两个日期。我假设您指的是 2017 年,因为那些日子不是 2016 年的周末。

declare @date date
while 1=1
begin
    select @date = DATEADD(DAY, ABS(CHECKSUM(NEWID()) % 8), '2017-02-07')
    if @date not in ('2/11/2017', '2/12/2017')
        break   
end
select @date

编辑看到评论...如果需要列表,您可以尝试以下操作: t-sql get all dates between 2 dates 然后只使用 where 子句过滤周末日期。

【讨论】:

    【解决方案2】:

    日历或 Tally 表可以解决问题,但我经常使用 TVF 来创建动态日期/时间范围。它是参数驱动的,您定义日期/时间范围、日期部分和增量

    示例

    Declare @D1 date = '2016-02-07'
    Declare @D2 date = '2016-02-14'
    
    Select Top 1 D=RetVal
     From  [dbo].[udf-Range-Date](@D1,@D2,'DD',1)
     Where DatePart(DW,RetVal) not in (7,1)
     Order By NewID()
    

    退货

    D
    2016-02-11 00:00:00.000
    

    有兴趣的 UDF

    CREATE FUNCTION [dbo].[udf-Range-Date] (@R1 datetime,@R2 datetime,@Part varchar(10),@Incr int)
    Returns Table
    Return (
        with cte0(M)   As (Select 1+Case @Part When 'YY' then DateDiff(YY,@R1,@R2)/@Incr When 'QQ' then DateDiff(QQ,@R1,@R2)/@Incr When 'MM' then DateDiff(MM,@R1,@R2)/@Incr When 'WK' then DateDiff(WK,@R1,@R2)/@Incr When 'DD' then DateDiff(DD,@R1,@R2)/@Incr When 'HH' then DateDiff(HH,@R1,@R2)/@Incr When 'MI' then DateDiff(MI,@R1,@R2)/@Incr When 'SS' then DateDiff(SS,@R1,@R2)/@Incr End),
             cte1(N)   As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
             cte2(N)   As (Select Top (Select M from cte0) Row_Number() over (Order By (Select NULL)) From cte1 a, cte1 b, cte1 c, cte1 d, cte1 e, cte1 f, cte1 g, cte1 h ),
             cte3(N,D) As (Select 0,@R1 Union All Select N,Case @Part When 'YY' then DateAdd(YY, N*@Incr, @R1) When 'QQ' then DateAdd(QQ, N*@Incr, @R1) When 'MM' then DateAdd(MM, N*@Incr, @R1) When 'WK' then DateAdd(WK, N*@Incr, @R1) When 'DD' then DateAdd(DD, N*@Incr, @R1) When 'HH' then DateAdd(HH, N*@Incr, @R1) When 'MI' then DateAdd(MI, N*@Incr, @R1) When 'SS' then DateAdd(SS, N*@Incr, @R1) End From cte2 )
    
        Select RetSeq = N+1
              ,RetVal = D 
         From  cte3,cte0 
         Where D<=@R2
    )
    /*
    Max 100 million observations -- Date Parts YY QQ MM WK DD HH MI SS
    Syntax:
    Select * from [dbo].[udf-Range-Date]('2016-10-01','2020-10-01','YY',1) 
    Select * from [dbo].[udf-Range-Date]('2016-01-01','2017-01-01','MM',1) 
    

    【讨论】:

      【解决方案3】:

      这样就可以了。我用过日期和转换函数。

      DECLARE @i INT = 0
      WHILE (@i<=7)
      BEGIN
       DECLARE @T TABLE
       (
          [DATE] DATE
       )
       INSERT INTO @T SELECT CONVERT (DATE,DATEADD(DAY,@i,'2016-02-07'))
       SET @i = @i+1
       IF(CONVERT (DATE,DATEADD(DAY,@i,'2016-02-07')) ='2016-02-11' OR CONVERT (DATE,DATEADD(DAY,@i,'2016-02-07')) ='2016-02-12')
        SET @i = @i+2
      END
      SELECT * FROM @T
      GO
      

      【讨论】:

        【解决方案4】:

        将@i 声明为int

        将@dr 声明为日期

        设置@i = 1

        而@i

        开始

        SELECT @dr = DATEADD(DAY, @i, '2016-02-07')

        而 (((DATEPART(dw, @dr) + @@DATEFIRST) % 7) 不在 (5, 6))

        begin
             print @dr
             break;
        end  
        

        设置@i = @i +1

        结束

        【讨论】:

          【解决方案5】:

          如果你给自己一个calendar table 并且你有一个非常简单的查询`

          SELECT TOP 1 Date 
          FROM Calendar 
          WHERE IsWeekday = 1 
          AND Date >= @StartDate 
          AND Date <= @EndDate 
          ORDER BY NEWID();
          

          不过,您总是可以即时生成日期:

          SET DATEFIRST 1;
          
          DECLARE @Start DATE = '20160207',
                  @End DATE = '20160214';
          
          WITH Calendar (Date) AS
          (   SELECT  TOP (DATEDIFF(DAY, @Start, @End) + 1)
                      DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY N1.N) - 1, @Start)
              FROM    (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n1 (N)
              CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n2 (N)
              CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n3 (N)
          )
          SELECT TOP 1 Date 
          FROM Calendar 
          WHERE DATEPART(WEEKDAY, Date) NOT IN (6, 7)
          ORDER BY NEWID();
          

          此处日历 CTE 交叉连接 3 个表值构造函数以生成最多 1,000 行 (10 x 10 x 10),然后将其限制为使用所需的天数

          TOP (DATEDIFF(DAY, @Start, @End) + 1)
          

          然后使用ROW_NUMBER() 生成从一开始的日期列表以生成从1 到n 的值。所以基本元素是:

          DECLARE @Start DATE = '20160207',
                  @End DATE = '20160214';
          
          WITH Calendar (Date) AS
          (   SELECT  TOP (DATEDIFF(DAY, @Start, @End) + 1)
                      DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY N1.N) - 1, @Start)
              FROM    (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n1 (N)
              CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n2 (N)
              CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n3 (N)
          )
          SELECT Date 
          FROM Calendar 
          

          这给出了:

          Date
          ------------
          2016-02-07
          2016-02-08
          2016-02-09
          2016-02-10
          2016-02-11
          2016-02-12
          2016-02-13
          

          这是一个简单的例子,用WHERE DATEPART(WEEKDAY, Date) NOT IN (6, 7) 删除周末并用TOP 1 ... ORDER BY NEWID() 选择一个随机行。顺便说一句,当使用像 DATEPART(WEEKDAY, ...) 这样敏感的设置时,您应该始终明确设置您需要的值,而不是依赖默认值。

          不过,我可能误解了您的要求,如果您只想要所有日期的列表,则不需要最后一步

          【讨论】:

            【解决方案6】:

            这样的事情怎么样:

            SELECT   inp.d AS [Start_Date]
                    ,incr.r AS Increment
                    ,DATEADD(DAY, incr.r, inp.d) AS New_Date
                    ,FORMAT(DATEADD(DAY, incr.r, inp.d), 'ddd') AS New_Date_Weekday
            FROM (
                -- Input date goes here
                SELECT   CAST('2016-02-07' AS DATE) AS d
            ) inp
            CROSS JOIN
                (
                    -- Generate 7 rows
                    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 1)) - 1 AS r
                    FROM (VALUES(1),(1),(1),(1),(1),(1),(1)) v(n)
            ) incr
            
            -- Convert week-day to 'Monday = 1 and Sunday = 7'-base;
            -- Select a random week-day between 1 and 5
            WHERE 1 + ( 5 + DATEPART(DW, DATEADD(DAY, incr.r, inp.d)) + @@DATEFIRST) % 7 = 1 + CAST(RAND() * 5 AS INT)
            

            感谢Kakkarot 的回答,他解释了如何将工作日计数转换为基于星期一 = 1 和星期日 = 7

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多