T-SQL - 如何在CTE中使用递归计算?

时间:2015-09-23 12:02:48

标签: tsql recursion common-table-expression

我想计算一名员工在为公司工作期间每月的平均收入。

我基于类似的线程构建了一个递归CTE,我只得到一个员工的结果。我需要做什么样的修改才能使员工ID变量并为表中的每个员工获得相同类型的结果?

这是我的代码:

DECLARE @tbl AS TABLE
(
  ID VARCHAR(50) ,
 Earning FLOAT,
  StartDate DATE ,
 EndDate DATE
)INSERT  INTO @tbl
    ( ID, Earning, StartDate, EndDate
SELECT  employee_ID  AS ID   
,([Total Earning]/ (SELECT datediff(MONTH,[EndDate],
[StartDate])+1
FROM [employee_table] WHERE employee_ID = 'EKA-0004562'))
,[StarTDate]
,[EndDate]
FROM [employee_table]
WHERE  employee_ID = 'EKA-0004562'


--final query using recursive cte
;
WITH    cte
          AS ( SELECT   T.ID ,
                    T.Earning ,
                    T.StartDate ,
                    T.EndDatum ,
                    CONVERT(DATE, NULL) AS Dt ,
                    n = 0
           FROM     @tbl AS T
           UNION ALL
           SELECT   cte.ID ,
                    cte.Earning ,
                    cte.StartDate ,
                    cte.EndDatum ,
                    DATEADD(MONTH, n, cte.StartDate) ,
                    cte.n + 1
           FROM     cte
           WHERE    n <= DATEDIFF(MONTH, cte.StartDate, cte.EndDatum)
         )
SELECT  cte.ID ,
        cte.Earning,
        dt AS Months
FROM    cte
WHERE   cte.Dt IS NOT NULL

这是我的目标:

好吧,我有一张包含大量数据的表格,其中包含有关员工的信息。 每个员工都有一个&#34; startdate&#34; (他开始为公司工作的时间)和&#34; enddate&#34; (他辞职的时候)。 我想在一个表中写入与员工在一个月内为公司工作的行数相同的行数。例如:

我的基本表:

Employee Number | StartDate | EndDate | Total Earnings (Total Earnings/(EndDate-StartDate)

4711               20150101   20150523        24110 

此示例显示该员工为公司工作了5个月并获得了24110欧元。在Avarage,他每月赚4822欧元。 所以我想在新表中插入5行,其中包含以下信息:

新表:

  Employee Number | StartDate | EndDate      |  AVG Earnings
  row1: 4711               20150101   20150523   4822

  row2: 4711               20150201   20150523   4822

  row3: 4711               20150301   20150523   4822

  row4: 4711               20150401   20150523   4822

  row5: 4711               20150501   20150523   4822

2 个答案:

答案 0 :(得分:0)

我可能错了,但我认为你的查询看起来过于复杂,不需要递归CTE。在与很多员工打交道并连续几年时,这可能会变得不合适。

此查询为2个示例用户提供正确的输出:

  • 样品

    declare @employee_table table(Employee_Number int, StartDate date, EndDate date, Total_Earnings bigint);
    Insert Into @employee_table(Employee_Number, StartDate, EndDate, Total_Earnings) values
    (4711, '20150101', '20150523', 24110)
    , (4712, '20150101', '20150423', 24110);
    
  • 查询

    with list(n) as (
        Select ROW_NUMBER() over(Order By n)
        From (
            Select 1 From (values(1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) as l1(n)
            Cross Join (values(1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) as l2(n)
            Cross Join (values(1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) as l3(n)
        ) as l(n)
    )
    Select l.n, e.Employee_Number
        , [StartDate] = DATEADD(MONTH, l.n-1, e.StartDate)
        , [EndDate] = e.EndDate
        , [AVG Earnings] = e.Total_Earnings/(DATEDIFF(MONTH, e.StartDate, e.EndDate)+1)
    From @employee_table as e
    Inner Join list as l on l.n <= DATEDIFF(MONTH, e.StartDate, e.EndDate)+1
    Order By e.Employee_Number, l.n
    
  

输出

n   Employee_Number StartDate   EndDate AVG Earnings
1   4711            2015-01-01  2015-05-23  4822
2   4711            2015-02-01  2015-05-23  4822
3   4711            2015-03-01  2015-05-23  4822
4   4711            2015-04-01  2015-05-23  4822
5   4711            2015-05-01  2015-05-23  4822
1   4712            2015-01-01  2015-04-23  6027
2   4712            2015-02-01  2015-04-23  6027
3   4712            2015-03-01  2015-04-23  6027
4   4712            2015-04-01  2015-04-23  6027
  

注释:

  • @employee_table包含2名员工。 4711与您的样本类似
  • 列表CTE有效地创建了一个从1到1000的列表,这个列表应该足够长达1000个月(> 83年)
  • 主查询使用它仅使用简单的INNER JOIN创建连续月份列表。

答案 1 :(得分:0)

你走了:

示例数据

create table #test
(
    ID INT,
    StartDate DATE, 
    EndDatum DATE,
    Earning INT
)
insert into #test values
(4711,'20150101','20150523',24110),
(4712,'20150101','20150625',32550)

QUERY

--final query using recursive cte
;
WITH    cte
          AS ( SELECT   T.ID ,
                    T.Earning ,
                    T.StartDate ,
                    T.EndDatum ,
                    CONVERT(DATE, NULL) AS Dt ,
                    n = 0
           FROM     #test AS T
           UNION ALL
           SELECT   cte.ID ,
                    cte.Earning ,
                    cte.StartDate ,
                    cte.EndDatum ,
                    DATEADD(MONTH, n, cte.StartDate) ,
                    cte.n + 1
           FROM     cte
           WHERE    n <= DATEDIFF(MONTH, cte.StartDate, cte.EndDatum)
         )
SELECT  cte.ID ,  
        dt StartDate,    
        cte.EndDatum EndDate,       
        cte.Earning / ((DATEDIFF(MONTH, cte.StartDate, cte.EndDatum)) +1) [AVG Earnings]
FROM    cte
WHERE   cte.Dt IS NOT NULL

<强>输出

ID      StartDate   EndDate     AVG Earnings
4712    2015-01-01  2015-06-25  5425
4712    2015-02-01  2015-06-25  5425
4712    2015-03-01  2015-06-25  5425
4712    2015-04-01  2015-06-25  5425
4712    2015-05-01  2015-06-25  5425
4712    2015-06-01  2015-06-25  5425
4711    2015-01-01  2015-05-23  4822
4711    2015-02-01  2015-05-23  4822
4711    2015-03-01  2015-05-23  4822
4711    2015-04-01  2015-05-23  4822
4711    2015-05-01  2015-05-23  4822