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

标签 sql sql-server sql-server-2008 t-sql 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

最佳答案

一种选择是使用临时统计表。计数/日历表也可以达到目的

我选择了 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

As Requested - Some Commentary

首先,我们创建 X 和 Y 之间的一系列日期。这是通过临时统计表、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 位于雇用日期和任期日期之间。这实际上创建了一个大型时间数据集。例如,如果一名员工仅活跃 10 天,我们将看到 10 条记录。每天 1 次。

然后我们执行一个简单的聚合COUNT(DISTINCT EmployeeID)按年和月分组。

关于sql - 获取每月活跃员工数量的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43875894/

相关文章:

sql - Oracle 根据年份选择日期返回不一致的结果

SQL 隔离级别、读写锁

sql-server - 在 Linux 中配置 ODBC 驱动程序

mysql - MS SQL - 遗留 Web 应用程序中的 MySQL 迁移

c# - 使用存储过程在单个表中插入 150 列

SQL Server 2008 - 查找具有最多行的表

mysql - 查询从具有添加约束的单个列中获取不同的对

mysql - 在单个查询中获取不同日期范围的多列计数

sql - T-SQL 在现有表中添加新列并填充来自另外两个现有列的值

sql - 如果使用附加列名再次创建删除的临时表,则无法选择附加列名