在SQL中生成转换模式

时间:2015-01-06 11:31:52

标签: sql sql-server with-statement

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

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

设定:

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

例如: 模式日: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天。 但我不知道如何确定日期范围是值班还是下班。

我如何创建用于创建行的循环直到模式结束日期?

任何帮助将不胜感激

3 个答案:

答案 0 :(得分:3)

正如您所提到的,您需要使用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

答案 1 :(得分:1)

我会使用一个数字表。

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

答案 2 :(得分:0)

我解决了这个问题

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