我的员工如下:
DECLARE @Employees TABLE
(
[EmployeeID] [int] IDENTITY(1,1) NOT NULL,
[HireDate] [datetime] NOT NULL,
[TerminationDate] [datetime] NULL
)
INSERT INTO @Employees (HireDate, TerminationDate) VALUES ('2016/01/01','2016/01/02')
INSERT INTO @Employees (HireDate, TerminationDate) VALUES ('2016/02/01', '2017/01/30')
INSERT INTO @Employees (HireDate, TerminationDate) VALUES ('2016/03/01', '2016/05/05')
如果我需要了解2016年2月的在职员工数量,我使用了以下查询:
SELECT * FROM @Employees
WHERE HireDate <= '2016-02-28' AND TerminationDate >= '2016-02-28'
但是,我很难找到一个简单的方法来找到每个月的活跃员工。例如,我想知道每个月从2016年1月到2017年1月的活跃员工数量。
我是否需要每个月都有单独的表格并使用一些CTE来交叉引用这两个表并提供每个月的报告?任何指示都将不胜感激。
到目前为止,我已经有了这个。它似乎工作得很好,除了2016年1月,我有一名员工活跃,但只有2天,因为我知道我正在验证月末,所以没有报告。任何调整?
DECLARE @startDate DATETIME
DECLARE @endDate datetime
SET @startDate='2014-01-31'
SET @endDate='2017-05-31'
DECLARE @Employees TABLE
(
[EmployeeID] [int] IDENTITY(1,1) NOT NULL,
[HireDate] [datetime] NOT NULL,
[TerminationDate] [datetime] NULL
)
INSERT INTO @Employees (HireDate, TerminationDate) VALUES ('2016/01/01','2016/01/02')
INSERT INTO @Employees (HireDate, TerminationDate) VALUES ('2016/02/01', '2017/01/30')
INSERT INTO @Employees (HireDate, TerminationDate) VALUES ('2016/03/01', '2016/05/05')
;With MyListOfDates( MyCalendarMonthEnd )
AS
(
SELECT @startDate MyCalendarMonthEnd
UNION ALL
SELECT DATEADD(MONTH, 1, MyCalendarMonthEnd)
FROM MyListOfDates
WHERE MyCalendarMonthEnd < @endDate
)
SELECT YEAR(mld.MyCalendarMonthEnd) Year, MONTH(mld.MyCalendarMonthEnd) Month, COUNT(*) ActiveEmployeeCount
FROM MyListOfDates mld
JOIN @Employees e on 1 = 1
WHERE e.HireDate <= mld.MyCalendarMonthEnd and e.TerminationDate >= mld.MyCalendarMonthEnd
GROUP BY mld.MyCalendarMonthEnd
答案 0 :(得分:3)
一种选择是使用ad-hoc计数表。一个计数/日历表也可以做到这一点
我选择了DatePart DAY捕捉当月的任何部分
示例强>
Declare @Date1 date = '2016-01-01'
Declare @Date2 date = '2017-01-31'
Select Year = DatePart(YEAR,D)
,Month = DatePart(MONTH,D)
,EmpCnt = count(DISTINCT [EmployeeID])
From (Select Top (DateDiff(DAY,@Date1,@Date2)+1) D=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),@Date1) From master..spt_values n1,master..spt_values n2) A
Left Join @Employees B on D between [HireDate] and IsNull([TerminationDate],GetDate())
Group By DatePart(YEAR,D), DatePart(MONTH,D)
Order By 1,2
<强>返回强>
Year Month EmpCnt
2016 1 1
2016 2 1
2016 3 2
2016 4 2
2016 5 2
2016 6 1
2016 7 1
2016 8 1
2016 9 1
2016 10 1
2016 11 1
2016 12 1
2017 1 1
根据要求 - 一些评论
首先,我们在X和Y之间创建一系列日期。这是通过ad-hoc计数表,Row_Number()和DateAdd()完成的。例如:
Declare @Date1 date = '2016-01-01'
Declare @Date2 date = '2017-01-31'
Select Top (DateDiff(DAY,@Date1,@Date2)+1) D=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),@Date1)
From master..spt_values n1,master..spt_values n2
返回
D
2016-01-01
2016-01-02
2016-01-03
2016-01-04
...
2017-01-29
2017-01-30
2017-01-31
请注意,我们正在对spt_values(n1和n2)执行交叉连接。这是因为spt_values只有2,523条记录(或几天)。考虑到这只相当于6年,通过使用交叉连接扩展潜在的630万天的时间跨度 - 一个荒谬的数字,但你永远不会看到该数量,因为我们指定TOP ( nDays )
一旦我们有了目标天数的数据集,我们就会对EMPLOYEE表执行LEFT JOIN,其中D介于Hire和Term日期之间。这实际上创建了一个大的时间数据集。例如,如果员工仅活动10天,我们将看到10条记录。每天1个。
然后我们按年和月执行一个简单的聚合COUNT(DISTINCT EmployeeID)
组。
答案 1 :(得分:0)
如果有人对使用CTE的解决方案感兴趣。首选解决方案由@JohnCappelleti提供
DECLARE @startDate DATETIME
DECLARE @endDate datetime
SET @startDate='2014-01-31'
SET @endDate='2017-05-31'
DECLARE @Employees TABLE
(
[EmployeeID] [int] IDENTITY(1,1) NOT NULL,
[HireDate] [datetime] NOT NULL,
[TerminationDate] [datetime] NULL
)
INSERT INTO @Employees (HireDate, TerminationDate) VALUES ('2016/01/01','2016/01/02')
INSERT INTO @Employees (HireDate, TerminationDate) VALUES ('2016/02/01', '2017/01/30')
INSERT INTO @Employees (HireDate, TerminationDate) VALUES ('2016/03/01', '2016/05/05')
;With MyListOfDates( MyCalendarMonthEnd )
AS
(
SELECT @startDate MyCalendarMonthEnd
UNION ALL
SELECT DATEADD(DAY, 1, MyCalendarMonthEnd)
FROM MyListOfDates
WHERE MyCalendarMonthEnd < @endDate
)
SELECT YEAR(mld.MyCalendarMonthEnd) Year, MONTH(mld.MyCalendarMonthEnd) Month, COUNT(DISTINCT EmployeeID) ActiveEmployeeCount
FROM MyListOfDates mld
JOIN @Employees e on 1 = 1
WHERE e.HireDate <= mld.MyCalendarMonthEnd and e.TerminationDate >= mld.MyCalendarMonthEnd
GROUP BY YEAR(mld.MyCalendarMonthEnd), MONTH(mld.MyCalendarMonthEnd)
ORDER BY 1,2
OPTION (MAXRECURSION 0)
答案 2 :(得分:0)
我已经查询@Techspider以表格形式解释输出。
我没有使用ROW_Number或不同。
我不使用CROSS Join因为我的输出是每个月,每年(不是每一天,每个月,每年)。
此外,你必须找到每个月的数量
同样找到这么长时间的计数会减慢
试试这个,
DECLARE @startDate DATETIME
DECLARE @endDate datetime
SET @startDate='2016-01-01'
SET @endDate='2017-01-31'
DECLARE @Employees TABLE
(
[EmployeeID] [int] IDENTITY(1,1) NOT NULL,
[HireDate] [datetime] NOT NULL,
[TerminationDate] [datetime] NULL
)
INSERT INTO @Employees (HireDate, TerminationDate) VALUES ('2016/01/01','2016/01/02')
INSERT INTO @Employees (HireDate, TerminationDate) VALUES ('2016/02/01', '2017/01/30')
INSERT INTO @Employees (HireDate, TerminationDate) VALUES ('2016/03/01', '2016/05/05')
SELECT datepart(year,EDT)[Year],datepart(month,edt)[Month]
,count( e.[EmployeeID]) EmpCount
FROM
(SELECT dateadd(month,number,@startDate)STDT
,dateadd(day,-1,dateadd(month,datediff(month,0,(dateadd(month,number,@startDate)))+1,0)) EDT
FROM MASTER.dbo.spt_values
WHERE name is null and number<=datediff(month,@startDate,@endDate)+1)n
left join @Employees E on
HireDate <= n.STDT
AND TerminationDate >= n.EDT
group by datepart(year,EDT),datepart(month,edt)
order by 1,2