PostgreSQL不同表结构的表按主键合并之神操作
Posted liuxiaoddd
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PostgreSQL不同表结构的表按主键合并之神操作相关的知识,希望对你有一定的参考价值。
这是一个很古老的问题,公司的研发人员也想了很久用了其他办法实现,但是效率非常低。
解释如下:
员工在8月份某天创建了多条拜访数据;
经理在8月份某天创建了多条拜访数据;
经理在8月份某天创建了多条协访数据;
拜访和协访存在于不同的表中,拜访是call,协访是coach_feedback。
现在要把这两张表的数据合并到一起,达到以下表头:
年月日,姓名,拜访次数,协访次数
拜访表:
日期 | 姓名 | 拜访次数 |
20190801 | A | 5 |
20190802 | A | 4 |
20190802 | B | 3 |
20190804 | B | 4 |
协访表:
日期 | 姓名 | 协访次数 |
20190802 | B | 1 |
20190803 | B | 3 |
目标表:
日期 | 姓名 | 拜访次数 | 协访次数 |
20190801 | A | 5 | 0 |
20190802 | A | 4 | 0 |
20190802 | B | 3 | 1 |
20190803 | B | 0 | 3 |
20190804 | B | 4 | 0 |
由于前提必须需要以拜访为主表,所以假设某一天经理没做拜访只做了协访,导致这一天的数据无法抓取。
重点来了:
1. 实现思路是,首先通过union把拜访表和协访表绑定到一起,自己没有的那一列均设为0
2. 第一遍过滤是判断两个次数相加大于0,至少产生过一次拜访或者协访
3. 由于union之后假如某天经理既做了拜访又做了协访,会导致出现两条重复数据,
4. 通过max函数取出较大的值(只要大于0就会取有效值,否则就是0)配合group by达到去重的目的。
具体的SQL如下(字段并非完全匹配,重在理解):
SELECT
user_id,
month_day,
MAX ( call_count ) call_count,
MAX ( coach_count ) coach_count
FROM
(
SELECT
"call".OWNER :: BIGINT as user_id,
to_char( to_timestamp( call_date / 1000 ) + INTERVAL '8 hours', 'yyyy-mm-dd' ) as month_day,
SUM ( CASE WHEN CALL.status is not null THEN 1 ELSE 0 END ) call_count,
0 AS coach_count
FROM
"call"
GROUP BY
CALL.OWNER,
month_day
UNION
(
SELECT
"coach_feedback".create_by :: BIGINT AS user_id,
to_char( to_timestamp( CAST ( coach_feedback.ext ->> 'sign_in_time' AS BIGINT ) / 1000 ) + INTERVAL '8 hours', 'yyyy-mm-dd' ) as month_day,
0 AS call_count,
SUM ( CASE WHEN coach_feedback.status IS NOT NULL THEN 1 ELSE 0 END ) coach_count
FROM
"coach_feedback"
GROUP BY
user_id,
month_day
)
) tmp_call_coach
WHERE
call_count + coach_count > 0
GROUP BY
user_id,
month_day
上面SQL跑出来的结果:
这个做完后,发现最简单的办法可能是通过user_info即人的总表来做full join,可能就不会有这么多弯弯,初步考虑是可行的,没有深挖,但是对当前问题来讲,是一个新的思路。
额外的收获:
A与B表union时,不能对两个表做order by 操作;
B表的列名会自动以A为准;
B表与A表必须结构一致至少列数一致;
union操作会自动根据相同的列且相同的值做合并,union all则会保留所有;
group by的引用其实除了sum还有max书本学的东西早都忘没了,妙用哉;
以上是关于PostgreSQL不同表结构的表按主键合并之神操作的主要内容,如果未能解决你的问题,请参考以下文章
ClickHouse ,选择 top n 并按主键排序的问题