【问题标题】:T-SQL - How to use a recursive calculation in a CTE?T-SQL - 如何在 CTE 中使用递归计算?
【发布时间】:2015-09-23 12:02:48
【问题描述】:

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

我基于一个类似的线程构建了一个递归 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

这是我的目标:

嗯,我有一张表格,里面有很多关于员工信息的数据。 每个员工都有一个“开始日期”(他开始为公司工作的时间)和“结束日期”(他辞职的时间)。 我想在表中写入与该员工在一个月内为公司工作的行数相同的行数。例如:

我的基本表:


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

【问题讨论】:

  • 为什么不直接选择employee_ID, avg(Earning) From [employee_table] Group By ID or Select [Total Earning]/ (datediff(MONTH,[EndDate], [StartDate])+1)
  • 因为如果员工在公司工作了 3 个月,我需要在 @TBL 表中使用公式(总收入/月数)中的 3 行所以在这种情况下,我们将有 3 次一行“总收入/3”。
  • 您可以添加 SQL Fiddle 或带有示例数据和输出的查询。我不确定我是否完全理解你的问题。 (只有一些数据,不是数千行:))
  • 提供样本数据和想要的结果。
  • 我用所需的样本数据编辑了我的第一篇文章。我希望你们明白我的意思:)

标签: tsql recursion common-table-expression


【解决方案1】:

给你:

样本数据

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

查询

--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

【讨论】:

    【解决方案2】:

    我可能错了,但我认为您的查询似乎过于复杂,不需要递归 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 创建连续月份的列表。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-02-04
      • 2015-08-12
      • 2020-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-31
      相关资源
      最近更新 更多