【问题标题】:Generating shift pattern in SQL在 SQL 中生成班次模式
【发布时间】:2015-03-04 01:35:02
【问题描述】:

我什至不知道如何开始解决这个问题,任何帮助将不胜感激:

我的目标是(动态地)生成一个返回工作班次日期的表格。

设置:

  • 我有一个模式如下 7 天上班 7 天下班,它继续...
  • 我可以确定班次开始的开始日期
  • 我可以确定模式天数(7 天 - 意味着 7 天,7 天休息)
  • 我可以确定计算模式的结束日期
  • 我要计算并创建下表

例如: 花样天数:7 开始日期:01/01/2015 模式结束日期:2015 年 12 月 31 日

ID         StartshiftDate        EndShiftDate     OnDuty
-------------------------------------------------------------
1           01/01/2015           01/07/2015       On Duty
2           01/08/2015           01/14/2015       Off Duty
3           01/15/2015           01/21/2015       On Duty

我知道我需要创建从开始日期开始的 CTE,我需要为每个日期添加 7 天。 但我不知道如何确定日期范围是上班还是下班。

以及我如何创建循环以创建行直到模式结束日期?

任何帮助将不胜感激

【问题讨论】:

  • 添加with-statement标签的任何具体原因?
  • 我的想法是我必须创建一个以 7 天为增量返回日期列表或日期列表的 with。我不想使用循环
  • 好吧,如果你这么认为,也许你应该试试。如果您坚持该声明,您至少可以在此处发布您的尝试,我们可以提供帮助。目前,您的问题似乎不知道从哪里开始,只是您已经专注于一个关键字。

标签: sql sql-server with-statement


【解决方案1】:

正如您提到的,您需要使用Recursive CTE。试试这个。

DECLARE @Startdate   DATE= '01/01/2015',
        @enddate     DATE = '01/31/2015',
        @Patterndays INT=7;

WITH cte
     AS (SELECT CONVERT(DATE, @Startdate)       [dates],
                1                               AS id,
                CONVERT(VARCHAR(20), 'On Duty') AS duty
         UNION ALL
         SELECT Dateadd(dd, @Patterndays, [dates]),
                ID + 1,
                CASE
                  WHEN ( id + 1 ) % 2 = 1 THEN CONVERT(VARCHAR(20), 'On Duty')
                  ELSE CONVERT(VARCHAR(20), 'Off Duty')
                END
         FROM   cte
         WHERE  dates < Dateadd(dd, -@Patterndays, CONVERT(DATE, @enddate)))
SELECT id,
       dates                AS StartshiftDate,
       Dateadd(DD, 6, dates)EndShiftDate,
       duty
FROM   cte 
Option (maxrecursion 0)

结果:

id  StartshiftDate  EndShiftDate    duty
--  --------------  ------------    -------
1   2015-01-01      2015-01-07      On Duty
2   2015-01-08      2015-01-14      Off Duty
3   2015-01-15      2015-01-21      On Duty
4   2015-01-22      2015-01-28      Off Duty
5   2015-01-29      2015-02-04      On Duty

【讨论】:

  • 您的答案的问题是,当您运行 2 年时,它会返回此错误语句终止。在语句完成之前,最大递归 100 已用完。
  • @silagy - 只需将Option (maxrecursion 0) 添加到CTE 的末尾即可,现在检查
  • 我将您的答案标记为正确答案,因为您先发布了它,我无法评估谁有更好的解决方案。谢谢!
【解决方案2】:

我会使用一个数字表。

http://web.archive.org/web/20150411042510/http://sqlserver2000.databases.aspfaq.com/why-should-i-consider-using-an-auxiliary-numbers-table.html

http://sqlperformance.com/2013/01/t-sql-queries/generate-a-set-1

This article 比较了不同的生成方式,包括递归 CTE,它比其他方式慢得多。

如何生成数字表并不重要,因为通常只生成一次。对于这个例子,我将使用上面文章中的一种方法填充一个包含 10000 个数字的表变量。

declare @TNumbers table (Number int);

INSERT INTO @TNumbers (Number)
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM sys.all_objects AS s1 CROSS JOIN sys.all_objects AS s2
OPTION (MAXDOP 1);

现在我们有一个数字表,生成您的日期是一个简单的公式:

DECLARE @VarStartDate date = '2015-01-01';
DECLARE @VarEndDate date = '2015-12-31';
DECLARE @VarShiftLength int = 7;

SELECT
    N.Number AS ID
    , DATEADD(day, (N.Number - 1) * @VarShiftLength, @VarStartDate) AS StartShiftDay
    , DATEADD(day, N.Number * @VarShiftLength - 1, @VarStartDate) AS EndShiftDay
    , CASE WHEN N.Number % 2 = 0 THEN 'Off Duty' ELSE 'On Duty' END AS OnDuty
FROM
    @TNumbers AS N
WHERE
    DATEADD(day, N.Number * @VarShiftLength - 1, @VarStartDate) <= @VarEndDate
ORDER BY ID;

结果集:

ID   StartShiftDay   EndShiftDay   OnDuty
1    2015-01-01      2015-01-07    On Duty
2    2015-01-08      2015-01-14    Off Duty
3    2015-01-15      2015-01-21    On Duty
4    2015-01-22      2015-01-28    Off Duty
5    2015-01-29      2015-02-04    On Duty
6    2015-02-05      2015-02-11    Off Duty
7    2015-02-12      2015-02-18    On Duty
8    2015-02-19      2015-02-25    Off Duty
9    2015-02-26      2015-03-04    On Duty
10   2015-03-05      2015-03-11    Off Duty
11   2015-03-12      2015-03-18    On Duty
12   2015-03-19      2015-03-25    Off Duty
13   2015-03-26      2015-04-01    On Duty
14   2015-04-02      2015-04-08    Off Duty
15   2015-04-09      2015-04-15    On Duty
16   2015-04-16      2015-04-22    Off Duty
17   2015-04-23      2015-04-29    On Duty
18   2015-04-30      2015-05-06    Off Duty
19   2015-05-07      2015-05-13    On Duty
20   2015-05-14      2015-05-20    Off Duty
21   2015-05-21      2015-05-27    On Duty
22   2015-05-28      2015-06-03    Off Duty
23   2015-06-04      2015-06-10    On Duty
24   2015-06-11      2015-06-17    Off Duty
25   2015-06-18      2015-06-24    On Duty
26   2015-06-25      2015-07-01    Off Duty
27   2015-07-02      2015-07-08    On Duty
28   2015-07-09      2015-07-15    Off Duty
29   2015-07-16      2015-07-22    On Duty
30   2015-07-23      2015-07-29    Off Duty
31   2015-07-30      2015-08-05    On Duty
32   2015-08-06      2015-08-12    Off Duty
33   2015-08-13      2015-08-19    On Duty
34   2015-08-20      2015-08-26    Off Duty
35   2015-08-27      2015-09-02    On Duty
36   2015-09-03      2015-09-09    Off Duty
37   2015-09-10      2015-09-16    On Duty
38   2015-09-17      2015-09-23    Off Duty
39   2015-09-24      2015-09-30    On Duty
40   2015-10-01      2015-10-07    Off Duty
41   2015-10-08      2015-10-14    On Duty
42   2015-10-15      2015-10-21    Off Duty
43   2015-10-22      2015-10-28    On Duty
44   2015-10-29      2015-11-04    Off Duty
45   2015-11-05      2015-11-11    On Duty
46   2015-11-12      2015-11-18    Off Duty
47   2015-11-19      2015-11-25    On Duty
48   2015-11-26      2015-12-02    Off Duty
49   2015-12-03      2015-12-09    On Duty
50   2015-12-10      2015-12-16    Off Duty
51   2015-12-17      2015-12-23    On Duty
52   2015-12-24      2015-12-30    Off Duty

【讨论】:

    【解决方案3】:

    我就这样解决了问题

    DECLARE @StartDate DATETIME = '2015-01-01'
    DECLARE @EndDate DATETIME = '2015-01-31'
    DECLARE @Pattern INT = 14
    
    ;WITH 
     N0 AS (SELECT 1 AS n UNION ALL SELECT 7)
    ,N1 AS (SELECT 1 AS n FROM N0 t1, N0 t2)
    ,N2 AS (SELECT 1 AS n FROM N1 t1, N1 t2)
    ,N3 AS (SELECT 1 AS n FROM N2 t1, N2 t2)
    ,N4 AS (SELECT 1 AS n FROM N3 t1, N3 t2)
    ,N5 AS (SELECT 1 AS n FROM N4 t1, N4 t2)
    ,N6 AS (SELECT 1 AS n FROM N5 t1, N5 t2)
    ,nums AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS num FROM N6),
    ShiftPattern AS (
    select
    ROW_NUMBER() OVER(ORDER BY num ASC) as ID,
    (ROW_NUMBER() OVER(ORDER BY num ASC) % 2) as d,
    DATEADD(day, -(@Pattern-1), DATEADD(day, num-1, @StartDate)) as Start,
    DATEADD(day, num-1, @StartDate) AS Enddate,
    CASE 
    WHEN (ROW_NUMBER() OVER(ORDER BY num ASC) % 2) = 1 THEN 'On Duty'
    WHEN (ROW_NUMBER() OVER(ORDER BY num ASC) % 2) = 0 THEN 'Off Duty'
    END AS 'Duty'
    FROM nums
    WHERE 
    (num % @Pattern) = 0 
    AND num <= DATEDIFF(day, @StartDate, @EndDate) + 1)
    
    SELECT *
    FROM ShiftPattern
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-03
      • 2021-04-04
      • 1970-01-01
      • 2010-09-20
      • 1970-01-01
      • 2016-10-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多