每月获得活跃员工数量的最佳方法是什么?

时间:2017-05-09 17:05:44

标签: sql sql-server sql-server-2008 tsql sql-server-2014

我的员工如下:

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

3 个答案:

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