SQL笛卡尔积查询的典型应用

SQL笛卡尔积查询的典型应用

作者:AntoniotheFuture

关键词:SQL,笛卡尔积

开发平台:不限

开发语言:SQL

简介: 介绍一个利用SQL笛卡尔积查询来简化代码的典型应用

 

我的日常工作中经常用到SQL,但没用过笛卡尔积查询,以前一直以为他只会出现在逻辑出错的时候,而且通常意味着大量的资源开销,不过今天有幸能够使用一次,而且发现他能简化代码,分享给大家:

现在有一张员工信息表,记录着从创办公司到现在所有员工的信息:

 

人员代码入职日期离职日期
0000012000-01-01 
0000022002-05-152008-04-09
0000032005-09-10 
0000042005-10-012014-03-15
...........

可能这时候就有DBA吐槽了,这个表不能这样设计,而是应该设计成任职记录表,每个员工多条记录,是我我也做成任职记录表,但是我们公司就是这样的

有一天老板心血来潮,他想看看我们公司的人力发展历史,就是统计公司自创办以来,每个月的在职人数,而且有个算法:当月入职或离职的算0.5个人,同月入职离职的不算在职。

以往我的思路是,将每个月的字段列出,然后分别判断该人员在当月是否在职,如果在职就为1,其他情况是0.5或者0,然后所有字段分别求和:

(case when to_date(入职日期,'yyyymm') < '200001' and to_date(离职日期,'yyyymm') >'200001' then
    1
when to_date(入职日期,'yyyymm') < '200001' and to_date(离职日期,'yyyymm') ='200001' then 
0.5
when to_date(入职日期,'yyyymm') < '200001' and to_date(离职日期,'yyyymm') <'200001' then 
0
when to_date(入职日期,'yyyymm') < '200001' and 离职日期 is null then 
1
when to_date(入职日期,'yyyymm') = '200001' and to_date(离职日期,'yyyymm') >'200001' then
0.5
when to_date(入职日期,'yyyymm') = '200001' and to_date(离职日期,'yyyymm') ='200001' then
0
when to_date(入职日期,'yyyymm') = '200001' and 离职日期 is null then
0.5
when to_date(入职日期,'yyyymm') > '200001' then
0
else
0
end) as 200001在职人力
(case when to_date(入职日期,'yyyymm') < '200002' and to_date(离职日期,'yyyymm') >'200002' then
    1
when to_date(入职日期,'yyyymm') < '200002' and to_date(离职日期,'yyyymm') ='200002' then 
0.5
when to_date(入职日期,'yyyymm') < '200002' and to_date(离职日期,'yyyymm') <'200002' then 
0
when to_date(入职日期,'yyyymm') < '200002' and 离职日期 is null then 
1
when to_date(入职日期,'yyyymm') = '200002' and to_date(离职日期,'yyyymm') >'200002' then
0.5
when to_date(入职日期,'yyyymm') = '200002' and to_date(离职日期,'yyyymm') ='200002' then
0
when to_date(入职日期,'yyyymm') = '200002' and 离职日期 is null then
0.5
when to_date(入职日期,'yyyymm') > '200002' then
0
else
0
end) as 200002在职人力
。。。。。

如果只求一两个月月到还好,但是现在要求所有月份,每年12个月12个字段,10年120个,代码虽然是规则的,但是也太长了,这么多字段,结果能不能输出还不一定呢。

于是我就想有没有优化的方案,灵光一闪,决定用笛卡尔积试试。

 

我们首先准备这样一张把所有月份列出来的表(可以从别的表group by而来):

月份
200001
200002
200003
.....

然后直接和员工信息表做笛卡尔积,结果集是这样的:

select * from 员工信息,月份表

人员代码入职日期离职日期月份
0000012000-01-01 200001
0000012000-01-01 200002
0000012000-01-01 200003
0000012000-01-01 200004
0000012000-01-01 200005
0000012000-01-01 200006
.............

就相当于列出了每个人员在每个月的情况,这个时候我们只需要做一个字段,根据入职日期,离职日期和月份计算该人员在该月份是否在职即可:

 

(case when to_date(入职日期,'yyyymm') < 月份 and to_date(离职日期,'yyyymm') >月份 then
    1
when to_date(入职日期,'yyyymm') < 月份 and to_date(离职日期,'yyyymm') =月份 then 
0.5
when to_date(入职日期,'yyyymm') < 月份 and to_date(离职日期,'yyyymm') <月份 then 
0
when to_date(入职日期,'yyyymm') < 月份 and 离职日期 is null then 
1
when to_date(入职日期,'yyyymm') = 月份 and to_date(离职日期,'yyyymm') >月份 then
0.5
when to_date(入职日期,'yyyymm') = 月份 and to_date(离职日期,'yyyymm') =月份 then
0
when to_date(入职日期,'yyyymm') = 月份 and 离职日期 is null then
0.5
when to_date(入职日期,'yyyymm') > 月份 then
0
else
0
end) as 当月在职人力

得到的结果集是这样的:

 

人员代码入职日期离职日期月份

当月在职人力

0000012000-01-01 2000010.5
0000012000-01-01 2000021
0000012000-01-01 2000031
0000012000-01-01 2000041
0000012000-01-01 2000051
0000012000-01-01 2000061
.............

 

然后就可以用月份group by 起来啦:

完整的伪代码如下,根据环境和不同的需求自行修改~

Select 
    月份,
    sum(case when to_date(入职日期,'yyyymm') < 月份 and to_date(离职日期,'yyyymm') >月份 then
        1
        when to_date(入职日期,'yyyymm') < 月份 and to_date(离职日期,'yyyymm') =月份 then 
        0.5
        when to_date(入职日期,'yyyymm') < 月份 and to_date(离职日期,'yyyymm') <月份 then 
        0
        when to_date(入职日期,'yyyymm') < 月份 and 离职日期 is null then 
        1
        when to_date(入职日期,'yyyymm') = 月份 and to_date(离职日期,'yyyymm') >月份 then
        0.5
        when to_date(入职日期,'yyyymm') = 月份 and to_date(离职日期,'yyyymm') =月份 then
        0
        when to_date(入职日期,'yyyymm') = 月份 and 离职日期 is null then
        0.5
        when to_date(入职日期,'yyyymm') > 月份 then
        0
        else
        0
        end
    ) as 当月在职人力
from 员工信息表,月份表
group by 月份

结果示例:

月份当月在职人力
201001350
201002364
201003360

201004

372
.........

 

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值