【问题标题】:Creating a timeline from 3 tables in SQL Server从 SQL Server 中的 3 个表创建时间线
【发布时间】:2015-09-28 23:27:12
【问题描述】:

我有 3 个包含事件日期的表格,我试图将它们组合起来以形成一个时间线表格。我不确定实现我需要的最佳方式,并希望能得到一些帮助。

我的第一个表是员工表。它包含四列

EmployeeId, HiredDate, CommencedDate, ResignDate, and FinishDate.

雇用员工时,他们会在表中获得一条记录,其中包含 EmployeeId 和 HiredDate 列。该表会在需要时不断更新。

CREATE TABLE Employees (EmployeeId INT NOT NULL, HiredDate DATE NOT NULL, CommencedDate DATE NULL, ResignDate DATE NULL, FinishDate DATE NULL)
Insert Employees Values (111, '2012-01-02', '2012-01-05', '2012-03-15', '2012-04-06')
Insert Employees Values (112, '2012-01-05', '2012-01-10', NULL, NULL)

名为 ReportingCycles 的第二个表包含 reportingCycleId, startDate, and endDate 列。报告周期中的天数会有所不同。

CREATE TABLE ReportingCycles (reportingCycleId INT NOT NULL, startDate DATE NOT NULL, endDate DATE NOT NULL)
INSERT ReportingCycles Values (1,'2011-12-03','2011-12-31')
INSERT ReportingCycles Values (2,'2012-01-01','2012-01-30')
INSERT ReportingCycles Values (3,'2012-01-31','2012-02-27')
INSERT ReportingCycles Values (4,'2012-02-28','2012-04-02')
INSERT ReportingCycles Values (5,'2012-04-03','2012-04-28')
INSERT ReportingCycles Values (6,'2012-04-29','2012-05-28')
INSERT ReportingCycles Values (7,'2012-05-29','2012-07-01')
INSERT ReportingCycles Values (8,'2012-07-02','2012-08-01')
INSERT ReportingCycles Values (9,'2012-08-02','2012-08-28')
INSERT ReportingCycles Values (10,'2012-08-29','2012-09-29')
INSERT ReportingCycles Values (11,'2012-09-30','2012-10-28')
INSERT ReportingCycles Values (12,'2012-10-29','2012-11-26')
INSERT ReportingCycles Values (13,'2012-11-27','2012-12-26')
INSERT ReportingCycles Values (14,'2012-12-27','2013-01-21')
INSERT ReportingCycles Values (15,'2013-01-22','2013-02-21')

我的第三个表称为“职责”,它为每个员工显示了员工在特定日期的位置。在员工已被雇用但尚未开始的情况下,这将填充一个虚拟位置。员工也可能在一个日期范围内位于多个位置,因此还会给出分配值。对于单个员工,日期范围的分配值的总和始终为 1。

CREATE TABLE Duties (EmployeeId INT NOT NULL, LocationId INT NOT NULL, AssignmentStartDate DATE NOT NULL, AssignmentEndDate DATE NULL, AssignmentValue Decimal(18,2) NOT NULL)
INSERT Duties Values (111, 0, '2012-01-02', '2012-01-04', 1.00)
INSERT Duties Values (111, 25, '2012-01-05', '2012-01-10', 1.00)
INSERT Duties Values (111, 30, '2012-01-11', '2012-04-06', 0.25)
INSERT Duties Values (111, 31, '2012-01-11', '2012-04-06', 0.25)
INSERT Duties Values (111, 32, '2012-01-11', '2012-04-06', 0.25)
INSERT Duties Values (111, 33, '2012-01-11', '2012-04-06', 0.25)
INSERT Duties Values (112, 0, '2012-01-05', '2012-01-09', 1)
INSERT Duties Values (112, 70, '2012-01-10', NULL, 1)

现在,我想做的是将所有这些表连接在一起,最终为每个员工制定一个时间表。结果表将包含以下列:

CREATE TABLE EmployeeTimelime (
fromDate DATE NOT NULL, 
toDate DATE NOT NULL, 
reportingCycleId INT NOT NULL, 
employeeId INT NOT NULL, 
LocationId INT NOT NULL, 
AssignmentValue Decimal(18,2),
Hired INT NOT NULL, 
Commenced INT NOT NULL, 
Resigned INT NOT NULL, 
Finished Int NOT NULL, 
startCycle INT NOT NULL, 
endCycle INT NOT NULL)

其中 Hired、Commenced、Resigned、Finished、startCycle 和 endCycle 列包含一个 0 或 1 值,显示它是什么类型的记录。 对于员工 111,表格如下所示:

Insert EmployeeTimelime Values ('2012-01-02','2012-01-04',2,111,0,1,1,0,0,0,0,0)
Insert EmployeeTimelime Values ('2012-01-05','2012-01-10',2,111,25,1,0,1,0,0,0,0)
Insert EmployeeTimelime Values ('2012-01-11','2012-01-30',2,111,30,0.25,0,0,0,0,0,1)
Insert EmployeeTimelime Values ('2012-01-11','2012-01-30',2,111,31,0.25,0,0,0,0,0,1)
Insert EmployeeTimelime Values ('2012-01-11','2012-01-30',2,111,32,0.25,0,0,0,0,0,1)
Insert EmployeeTimelime Values ('2012-01-11','2012-01-30',2,111,33,0.25,0,0,0,0,0,1)
Insert EmployeeTimelime Values ('2012-01-31','2012-02-27',3,111,30,0.25,0,0,0,0,1,1)
Insert EmployeeTimelime Values ('2012-01-31','2012-02-27',3,111,31,0.25,0,0,0,0,1,1)
Insert EmployeeTimelime Values ('2012-01-31','2012-02-27',3,111,32,0.25,0,0,0,0,1,1)
Insert EmployeeTimelime Values ('2012-01-31','2012-02-27',3,111,33,0.25,0,0,0,0,1,1)
Insert EmployeeTimelime Values ('2012-02-28','2012-03-14',4,111,30,0.25,0,0,0,0,1,0)
Insert EmployeeTimelime Values ('2012-02-28','2012-03-14',4,111,31,0.25,0,0,0,0,1,0)
Insert EmployeeTimelime Values ('2012-02-28','2012-03-14',4,111,32,0.25,0,0,0,0,1,0)
Insert EmployeeTimelime Values ('2012-02-28','2012-03-14',4,111,33,0.25,0,0,0,0,1,0)
Insert EmployeeTimelime Values ('2012-03-15','2012-04-02',4,111,30,0.25,0,0,1,0,0,1)
Insert EmployeeTimelime Values ('2012-03-15','2012-04-02',4,111,31,0.25,0,0,1,0,0,1)
Insert EmployeeTimelime Values ('2012-03-15','2012-04-02',4,111,32,0.25,0,0,1,0,0,1)
Insert EmployeeTimelime Values ('2012-03-15','2012-04-02',4,111,33,0.25,0,0,1,0,0,1)
Insert EmployeeTimelime Values ('2012-04-03','2012-04-05',5,111,30,0.25,0,0,0,0,1,0)
Insert EmployeeTimelime Values ('2012-04-03','2012-04-05',5,111,31,0.25,0,0,0,0,1,0)
Insert EmployeeTimelime Values ('2012-04-03','2012-04-05',5,111,32,0.25,0,0,0,0,1,0)
Insert EmployeeTimelime Values ('2012-04-03','2012-04-05',5,111,33,0.25,0,0,0,0,1,0)
Insert EmployeeTimelime Values ('2012-04-06','2012-04-06',5,111,30,0.25,0,0,0,1,0,0)
Insert EmployeeTimelime Values ('2012-04-06','2012-04-06',5,111,31,0.25,0,0,0,1,0,0)
Insert EmployeeTimelime Values ('2012-04-06','2012-04-06',5,111,32,0.25,0,0,0,1,0,0)
Insert EmployeeTimelime Values ('2012-04-06','2012-04-06',5,111,33,0.25,0,0,0,1,0,0)

我从这个查询开始,但它不正确:

SELECT *
FROM Employees x

Join ReportingCycles y
on (y.startDate between x.HiredDate and Coalesce(x.FinishDate, '9999-12-31') 
or y.endDate between x.HiredDate and Coalesce(x.FinishDate, '9999-12-31') )

Left Join Duties z
on z.EmployeeId = x.EmployeeId 
and (z.AssignmentStartDate  between y.startDate and y.endDate 
or Coalesce(z.AssignmentEndDate, '9999-12-31') between y.startDate and y.endDate 
)

Order by x.EmployeeId, y.reportingCycleId

感谢任何可以提供帮助的人。

【问题讨论】:

  • 您的预期结果是什么?

标签: sql-server tsql timeline


【解决方案1】:
---------------------------------------------------
-- Preparation of Data
---------------------------------------------------
DECLARE @EmployeeTimelime table (
    fromDate DATE NOT NULL, 
    toDate DATE NOT NULL, 
    reportingCycleId INT NOT NULL, 
    employeeId INT NOT NULL, 
    LocationId INT NOT NULL, 
    AssignmentValue Decimal(18,2),

    -- Should be using bit data type for boolean values
    Hired INT NOT NULL,
    Commenced INT NOT NULL, 
    Resigned INT NOT NULL, 
    Finished Int NOT NULL, 
    startCycle INT NOT NULL, 
    endCycle INT NOT NULL 
)

DECLARE @Employees table (employeeId INT NOT NULL, HiredDate DATE NOT NULL, CommencedDate DATE NULL, ResignDate DATE NULL, FinishDate DATE NULL)
INSERT @Employees Values 
(111, '2012-01-02', '2012-01-05', '2012-03-15', '2012-04-06')
,(112, '2012-01-05', '2012-01-10', NULL, NULL)

DECLARE @ReportingCycles table (reportingCycleId INT NOT NULL, startDate DATE NOT NULL, endDate DATE NOT NULL)
INSERT @ReportingCycles Values 
(1,'2011-12-03','2011-12-31')
,(2,'2012-01-01','2012-01-30')
,(3,'2012-01-31','2012-02-27')
,(4,'2012-02-28','2012-04-02')
,(5,'2012-04-03','2012-04-28')
,(6,'2012-04-29','2012-05-28')
,(7,'2012-05-29','2012-07-01')
,(8,'2012-07-02','2012-08-01')
,(9,'2012-08-02','2012-08-28')
,(10,'2012-08-29','2012-09-29')
,(11,'2012-09-30','2012-10-28')
,(12,'2012-10-29','2012-11-26')
,(13,'2012-11-27','2012-12-26')
,(14,'2012-12-27','2013-01-21')
,(15,'2013-01-22','2013-02-21')

DECLARE @Duties table (EmployeeId INT NOT NULL, LocationId INT NOT NULL, AssignmentStartDate DATE NOT NULL, AssignmentEndDate DATE NULL, AssignmentValue Decimal(18,2) NOT NULL)
INSERT @Duties Values 
(111, 0, '2012-01-02', '2012-01-04', 1.00)
,(111, 25, '2012-01-05', '2012-01-10', 1.00)
,(111, 30, '2012-01-11', '2012-04-06', 0.25)
,(111, 31, '2012-01-11', '2012-04-06', 0.25)
,(111, 32, '2012-01-11', '2012-04-06', 0.25)
,(111, 33, '2012-01-11', '2012-04-06', 0.25)
,(112, 0, '2012-01-05', '2012-01-09', 1)
,(112, 70, '2012-01-10', NULL, 1)


---------------------------------------------------
-- The logic lays below
---------------------------------------------------

;WITH dates(Value, EndDate) AS -- tally table of dates
(
    SELECT MIN(HiredDate), MAX(ISNULL(ResignDate, GETDATE())) FROM @Employees
    UNION ALL
    SELECT DATEADD(DAY, 1, Value), EndDate FROM dates WHERE DATEADD(DAY, 1, Value) <= EndDate
), data AS -- Prepare the non-grouped data, with well declared relations
(
    SELECT 
        d.Value, e.employeeId, rc.reportingCycleId, du.LocationId, du.AssignmentValue, 
        CASE WHEN e.HiredDate = d.Value THEN 1 ELSE 0 END AS HiredFlag,
        CASE WHEN e.CommencedDate = d.Value THEN 1 ELSE 0 END AS CommencedFlag,
        CASE WHEN e.ResignDate >= d.Value THEN 1 ELSE 0 END AS ResignFlag,
        CASE WHEN e.FinishDate = d.Value THEN 1 ELSE 0 END AS FinishFlag, 
        CASE WHEN rc.startDate = d.Value THEN 1 ELSE 0 END AS CycleStartFlag, 
        CASE WHEN rc.endDate = d.Value THEN 1 ELSE 0 END AS CycleEndFlag, 
        -- Magically group by continuous dates by employee and location
        ROW_NUMBER() OVER (PARTITION BY e.employeeId, du.LocationId ORDER BY Value) rn_loc, 
        DENSE_RANK() OVER (PARTITION BY e.employeeId ORDER BY Value) rn
        -- End Magic
    FROM 
        @Employees e 
        CROSS JOIN dates d
        INNER JOIN @Duties du ON d.Value BETWEEN du.AssignmentStartDate AND ISNULL(du.AssignmentEndDate, '9999-12-31') AND du.employeeId = e.employeeId
        INNER JOIN @ReportingCycles rc ON d.Value BETWEEN rc.startDate AND rc.endDate
)
INSERT @EmployeeTimelime
SELECT 
    MIN(Value) AS fromDate, MAX(Value) AS toDate, reportingCycleId, employeeId, locationId, assignmentValue, 
    MAX(HiredFlag) AS Hired,
    MAX(CommencedFlag) AS Commenced,
    ResignFlag AS Resigned,
    FinishFlag AS Finished,
    MAX(CycleStartFlag) AS startCycle,
    MAX(CycleEndFlag) AS endCycle
FROM data
GROUP BY EmployeeId, reportingCycleId, locationId, assignmentValue, ResignFlag, FinishFlag, rn - rn_loc
--ORDER BY fromDate, toDate, employeeId, reportingCycleId, LocationId
OPTION (MAXRECURSION 0)

SELECT * FROM @EmployeeTimelime WHERE employeeId = '111'

SQL Fiddle

【讨论】:

  • 太棒了。非常棒。谢谢你
  • 嗨,埃里克。我只是好奇为什么在GROUP BY EmployeeId, reportingCycleId, locationId, assignmentValue, ResignFlag, FinishFlag, rn - rn_loc 这一行中,你按ResignFlag, FinishFlag 分组,而不是HiredFlag and CommencedFlag。那部分让我很头疼。
  • @user3634559 我只是从您的示例结果中猜测,如果您需要通过除 Resign 和 Finish 以外的任何标志分隔结果行,您可以添加到组中并更新 CASE在数据中
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-24
  • 1970-01-01
  • 2011-11-03
  • 1970-01-01
相关资源
最近更新 更多