SQL Server - 按计数分割时间的查询(重叠的办公室)

时间:2014-07-09 08:54:38

标签: sql sql-server database tsql

我正在寻找关于我应该对查询采取的方法的一些建议。我有一张桌子(EMP),用于存储今年的员工详细信息和工作时间(每周40小时)。另外两个表存储员工所属的主要和二级办公室。由于员工可以在办公室之间移动,因此会存储日期。

我希望在员工在办公室期间返回工作时数。如果主要办公室与雇员的二级办公室重叠,则应按重叠期间的重叠办公室数量分配小时数。

我在下面附上样本DDL。

-- Employee Table with hours for year 2014

CREATE TABLE [dbo].[EMP](
    [EMP_ID] [int] NOT NULL,
    [EMP_NAME] [varchar](255) NULL,
    [EMP_FYHOURS] [float] NULL,
 CONSTRAINT [PK_EMP] PRIMARY KEY CLUSTERED 
(
    [EMP_ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY]

GO

-- Employees and their primary offices

CREATE TABLE [dbo].[OFFICEPRIMARY](
    [OFFICEPRIMARY_ID] [int] NOT NULL,
    [OFFICEPRIMARY_NAME] [varchar](255) NULL,
    [OFFICEPRIMARY_EMP_ID] [int] NOT NULL,
    [OFFICEPRIMARY_START] [datetime] NULL,
    [OFFICEPRIMARY_END] [datetime] NULL,
 CONSTRAINT [PK_OFFICEPRIMARY] PRIMARY KEY CLUSTERED 
(
    [OFFICEPRIMARY_ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

ALTER TABLE [dbo].[OFFICEPRIMARY]  WITH CHECK ADD  CONSTRAINT [FK_OFFICEPRIMARY_FK1] FOREIGN KEY([OFFICEPRIMARY_EMP_ID])
REFERENCES [dbo].[EMP] ([EMP_ID])
ON DELETE CASCADE
GO

ALTER TABLE [dbo].[OFFICEPRIMARY] CHECK CONSTRAINT [FK_OFFICEPRIMARY_FK1]
GO

-- Employees and their secondary offices

CREATE TABLE [dbo].[OFFICESECONDARY](
    [OFFICESECONDARY_ID] [int] NOT NULL,
    [OFFICESECONDARY_NAME] [varchar](255) NULL,
    [OFFICESECONDARY_EMP_ID] [int] NOT NULL,
    [OFFICESECONDARY_START] [datetime] NULL,
    [OFFICESECONDARY_END] [datetime] NULL,
 CONSTRAINT [PK_OFFICESECONDARY] PRIMARY KEY CLUSTERED 
(
    [OFFICESECONDARY_ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

ALTER TABLE [dbo].[OFFICESECONDARY]  WITH CHECK ADD  CONSTRAINT [FK_OFFICESECONDARY_FK1] FOREIGN KEY([OFFICESECONDARY_EMP_ID])
REFERENCES [dbo].[EMP] ([EMP_ID])
ON DELETE CASCADE
GO

ALTER TABLE [dbo].[OFFICESECONDARY] CHECK CONSTRAINT [FK_OFFICESECONDARY_FK1]
GO

-- Insert sample data

INSERT INTO EMP (EMP_ID, EMP_NAME, EMP_FYHOURS)
VALUES (1, 'John Smith', 2080);

INSERT INTO EMP (EMP_ID, EMP_NAME, EMP_FYHOURS)
VALUES (2, 'Jane Doe', 2080);

GO

INSERT INTO OFFICEPRIMARY (OFFICEPRIMARY_ID, OFFICEPRIMARY_NAME, OFFICEPRIMARY_EMP_ID, OFFICEPRIMARY_START, OFFICEPRIMARY_END)
VALUES (1, 'London', 1, '2014-01-01', '2014-05-31')

INSERT INTO OFFICEPRIMARY (OFFICEPRIMARY_ID, OFFICEPRIMARY_NAME, OFFICEPRIMARY_EMP_ID, OFFICEPRIMARY_START, OFFICEPRIMARY_END)
VALUES (2, 'Berlin', 1, '2014-06-01', '2014-08-31')

INSERT INTO OFFICEPRIMARY (OFFICEPRIMARY_ID, OFFICEPRIMARY_NAME, OFFICEPRIMARY_EMP_ID, OFFICEPRIMARY_START, OFFICEPRIMARY_END)
VALUES (3, 'New York', 1, '2014-09-01', '2014-12-31')

INSERT INTO OFFICEPRIMARY (OFFICEPRIMARY_ID, OFFICEPRIMARY_NAME, OFFICEPRIMARY_EMP_ID, OFFICEPRIMARY_START, OFFICEPRIMARY_END)
VALUES (4, 'New York', 2, '2014-01-01', '2014-04-15')

INSERT INTO OFFICEPRIMARY (OFFICEPRIMARY_ID, OFFICEPRIMARY_NAME, OFFICEPRIMARY_EMP_ID, OFFICEPRIMARY_START, OFFICEPRIMARY_END)
VALUES (5, 'Paris', 2, '2014-04-16', '2014-09-30')

INSERT INTO OFFICEPRIMARY (OFFICEPRIMARY_ID, OFFICEPRIMARY_NAME, OFFICEPRIMARY_EMP_ID, OFFICEPRIMARY_START, OFFICEPRIMARY_END)
VALUES (6, 'London', 2, '2014-10-01', '2014-12-31')

GO

INSERT INTO OFFICESECONDARY (OFFICESECONDARY_ID, OFFICESECONDARY_NAME, OFFICESECONDARY_EMP_ID, OFFICESECONDARY_START, OFFICESECONDARY_END)
VALUES (1, 'Paris', 1, '2014-01-01', '2014-03-31')

INSERT INTO OFFICESECONDARY (OFFICESECONDARY_ID, OFFICESECONDARY_NAME, OFFICESECONDARY_EMP_ID, OFFICESECONDARY_START, OFFICESECONDARY_END)
VALUES (2, 'Lyon', 1, '2014-04-01', '2014-05-15')

INSERT INTO OFFICESECONDARY (OFFICESECONDARY_ID, OFFICESECONDARY_NAME, OFFICESECONDARY_EMP_ID, OFFICESECONDARY_START, OFFICESECONDARY_END)
VALUES (3, 'Berlin', 1, '2014-05-16', '2014-09-30')

INSERT INTO OFFICESECONDARY (OFFICESECONDARY_ID, OFFICESECONDARY_NAME, OFFICESECONDARY_EMP_ID, OFFICESECONDARY_START, OFFICESECONDARY_END)
VALUES (4, 'Chicago', 1, '2014-10-01', '2015-02-22')

INSERT INTO OFFICESECONDARY (OFFICESECONDARY_ID, OFFICESECONDARY_NAME, OFFICESECONDARY_EMP_ID, OFFICESECONDARY_START, OFFICESECONDARY_END)
VALUES (5, 'Chicago', 2, '2013-11-21', '2014-04-10')

INSERT INTO OFFICESECONDARY (OFFICESECONDARY_ID, OFFICESECONDARY_NAME, OFFICESECONDARY_EMP_ID, OFFICESECONDARY_START, OFFICESECONDARY_END)
VALUES (6, 'Berlin', 2, '2014-04-11', '2014-09-16')

INSERT INTO OFFICESECONDARY (OFFICESECONDARY_ID, OFFICESECONDARY_NAME, OFFICESECONDARY_EMP_ID, OFFICESECONDARY_START, OFFICESECONDARY_END)
VALUES (7, 'Amsterdam', 2, '2014-09-17', '2015-03-31')

GO

感谢指针。我调整了您的查询,因此它提供了主要和次要办公室的联合。

剩下的就是计算办公室之间重叠时间的时间。例如,

John Smith,纽约,2014年4月1日,2014年8月10日

约翰史密斯,伦敦,2014年8月1日,2014年12月31日

对于2014年8月1日至2014年8月10日办公室之间的重叠期,我希望将时间平均分配。如果有3个重叠的办公室,那么它将被分成3路。

select 'Primary' as Office, e.EMP_NAME, op.OFFICEPRIMARY_NAME, op.OFFICEPRIMARY_START, op.OFFICEPRIMARY_END, datediff(wk,OFFICEPRIMARY_START,OFFICEPRIMARY_END) * 40 as HoursWorkedPrimary
from EMP e
inner join OFFICEPRIMARY op on op.OFFICEPRIMARY_EMP_ID = e.EMP_ID
union all
select 'Secondary' as Office, e.EMP_NAME, os.OFFICESECONDARY_NAME, os.OFFICESECONDARY_START, os.OFFICESECONDARY_END, datediff(wk,OFFICESECONDARY_START,OFFICESECONDARY_END) * 40 as HoursWorkedSecondary
from EMP e
inner join OFFICESECONDARY os on os.OFFICESECONDARY_EMP_ID = e.EMP_ID
order by e.EMP_NAME

3 个答案:

答案 0 :(得分:0)

这应该让你有个先机:

select datediff(wk,OFFICEPRIMARY_START,OFFICEPRIMARY_END) * 40 as HoursWorkedPrimary
,datediff(wk,OFFICESECONDARY_START,OFFICESECONDARY_END) * 40 as HoursWorkedSecondary
,EMP_NAME
,OFFICEPRIMARY_NAME,OFFICEPRIMARY_START,OFFICEPRIMARY_END
,OFFICESECONDARY_NAME,OFFICESECONDARY_START,OFFICESECONDARY_END
from [EMP]
inner join OFFICEPRIMARY as op on op.OFFICEPRIMARY_EMP_ID = EMP.EMP_ID
inner join OFFICESECONDARY as os on os.OFFICESECONDARY_EMP_ID = EMP.EMP_ID

答案 1 :(得分:0)

以下链接可帮助您找到确定日期重叠方向的正确方向。

Count days in date range with set of exclusions which may overlap

答案 2 :(得分:0)

如果我理解正确,您希望看到的最终结果是每位员工和办公室的总工作小时数?

我想出了这个:

-- generate date table
declare @MinDate datetime, @MaxDate datetime
SET @MinDate = (SELECT MIN(d) FROM (SELECT d = OFFICEPRIMARY_START FROM dbo.OFFICEPRIMARY UNION SELECT OFFICESECONDARY_START FROM dbo.OFFICESECONDARY) a)
SET @MaxDate = (SELECT MAX(d) FROM (SELECT d = OFFICEPRIMARY_END FROM dbo.OFFICEPRIMARY UNION SELECT OFFICESECONDARY_END FROM dbo.OFFICESECONDARY) a)

SELECT
    d = DATEADD(day, number, @MinDate)
INTO
    #tmp_dates
FROM 
    (SELECT DISTINCT number FROM master.dbo.spt_values WHERE name IS NULL) n
WHERE
    DATEADD(day, number, @MinDate) < @MaxDate


;WITH CTE AS
(
SELECT  
    d.d
    ,o.OfficeType
    ,o.OfficeID
    ,o.OfficeName
    ,o.EmpID
    ,EmpName = e.EMP_NAME
    ,HoursWorked = 8 / (COUNT(1) OVER (PARTITION BY EmpID, d))
FROM
    (
        SELECT
            OfficeType = 1
            ,OfficeID = op.OFFICEPRIMARY_ID
            ,OfficeName = op.OFFICEPRIMARY_NAME
            ,EmpID = op.OFFICEPRIMARY_EMP_ID
            ,StartDate = op.OFFICEPRIMARY_START
            ,EndDate = op.OFFICEPRIMARY_END
        FROM 
            dbo.OFFICEPRIMARY op

        UNION

        SELECT
            OfficeType = 2
            ,OfficeID = os.OFFICESECONDARY_ID
            ,OfficeName = os.OFFICESECONDARY_NAME
            ,EmpID = os.OFFICESECONDARY_EMP_ID
            ,StartDate = os.OFFICESECONDARY_START
            ,EndDate = os.OFFICESECONDARY_END
        FROM 
            dbo.OFFICESECONDARY os
    ) o

INNER JOIN
    dbo.EMP e ON e.EMP_ID = o.EmpID
INNER JOIN
    #tmp_dates d ON o.StartDate<=d.d AND o.EndDate>=d.d
)

SELECT
    EmpID
    ,EmpName
    ,OfficeType
    ,OfficeName
    ,TotalHoursWorked = SUM(HoursWorked)
FROM
    CTE
GROUP BY
    EmpID
    ,EmpName
    ,OfficeType
    ,OfficeID
    ,OfficeName
ORDER BY
    EmpID
    ,OfficeName

我首先生成一个临时表,其中包含最小日期和最大日期之间的日期。

然后我联合两个办公桌(为什么你还有两张桌子?)我得到一份CTE,它返回员工,日期,办公室和办公室工作小时数据(8除以员工所在的办公室数量)在这一天工作。)

然后我将这些数据相加得到按员工和办公室分组的小时数。

也许有一个更简单的解决方案。这是我想到的第一个解决方案。