假设我有一个“员工”表,其中包含员工的开始和结束日期,如下所示:
员工
employee_id start_date end_date
53 '19901117' '99991231'
54 '19910208' '20010512'
55 '19910415' '20120130'
. . .
. . .
. . .
假设我想获取月底受雇的员工的月度数量。因此,我需要的结果数据集将如下所示:
month count of employees
'20150131' 120
'20150228' 118
'20150331' 122
. .
. .
. .
我目前知道如何执行此操作的最佳方法是创建一个要连接到的“帮助程序”表,例如:
helper_tbl
month
'20150131'
'20150228'
'20150331'
.
.
.
然后执行如下查询:
SELECT t0b.month,
count(t0a.employee_id)
FROM employees t0a
JOIN helper_tbl t0b
ON t0b.month BETWEEN t0a.start_date AND t0a.end_date
GROUP BY t0b.month
但是,这对我来说是个烦人的解决方案,因为这意味着我必须一直创建这些小助手表,并且它们会使我的架构混乱。我觉得其他人也必须遇到同样需要“ helper”表的情况,但是我猜人们已经找到了一种更好的方法来解决此问题,而这并不是那么手工。还是你们真的像我一样一直在创建“ helper”表来解决这些情况?
我知道这个问题对于堆栈溢出来说有点开放,所以让我提供一个更为封闭的问题版本:“ 仅给出'employees'表,您会怎么做?以获得上面显示的结果数据集?”
答案 0 :(得分:1)
您可以使用CTE生成所有月份值,这些值可以是固定的起点,也可以基于表格中最早的日期生成:
with months (month) as (
select add_months(first_month, level - 1)
from (
select trunc(min(start_date), 'MM') as first_month from employees
)
connect by level <= ceil(months_between(sysdate, first_month))
)
select * from months;
以您的示例中最早的开始日期为1990-11-17的数据为例,它会生成333行:
MONTH
-------------------
1990-11-01 00:00:00
1990-12-01 00:00:00
1991-01-01 00:00:00
1991-02-01 00:00:00
1991-03-01 00:00:00
...
2018-06-01 00:00:00
2018-07-01 00:00:00
然后,您可以在连接到表的查询中使用它,例如:
with months (month) as (
select add_months(first_month, level - 1)
from (
select trunc(min(start_date), 'MM') as first_month from employees
)
connect by level <= ceil(months_between(sysdate, first_month))
)
select m.month, count(*) as employees
from months m
left join employees e
on e.start_date <= add_months(m.month, 1)
and (e.end_date is null or e.end_date >= add_months(m.month, 1))
group by m.month
order by m.month;
想必您希望包括仍在工作的人员,因此您需要允许结束日期为空(除非您对仍在工作的人员使用不可思议的结束日期值...)
将日期存储为字符串会有点复杂,但是您可以通过类似的方式生成月份信息:
with months (month, start_date, end_date) as (
select add_months(first_month, level - 1),
to_char(add_months(first_month, level - 1), 'YYYYMMDD'),
to_char(last_day(add_months(first_month, level - 1)), 'YYYYMMDD')
from (
select trunc(min(to_date(start_date, 'YYYYMMDD')), 'MM') as first_month from employees
)
connect by level <= ceil(months_between(sysdate, first_month))
)
select m.month, m.start_date, m.end_date, count(*) as employees
from months m
left join employees e
on e.start_date <= m.end_date
and (e.end_date is null or e.end_date > m.end_date)
group by m.month, m.start_date, m.end_date
order by m.month;
使用少量的虚假数据进行了非常轻松的测试,并且似乎都可以正常工作。
答案 1 :(得分:0)
如果要获取本月底雇用的员工,则可以在查询的WHERE子句中使用LAST_DAY函数。另外,您可以在查询的GROUP BY子句中使用该函数。因此您的查询将如下所示,
SELECT LAST_DAY(start_date), COUNT(1)
FROM employees
WHERE start_date = LAST_DAY(start_date)
GROUP BY LAST_DAY(start_date)
或者如果您只想计算每月雇用的员工,则使用以下查询,
SELECT LAST_DAY(start_date), COUNT(1)
FROM employees
GROUP BY LAST_DAY(start_date)