[英]Left join when left table does not have all group by columns
我发现很难用精确的措词来表达这个问题。
因此,为简化起见:我有两个表,将它们event_type
和player_events
。 第一个表是事件类型(字符串)的列表,称为列event_type.name
。
player_events
是单个玩家事件的列表。 有些玩家可能会错过一些事件,并且同一位玩家可能会发生多次事件。 因此, player_events
具有相关的列player_events.player_id
和player_events.event_type_name
,以及其他与创建时间等有关的列,但后者是不相关的。
我想要一个玩家事件类型的计数,包括零。 我需要区分每个玩家的不同事件类型。 所以最后我应该得到这样的东西:
player_id event_type_name player_event_count 0 LoginEvent 1 0 ProfileChangedEvent 0 1 LoginEvent 5 1 ProfileChangedEvent 1 ...
我当时想离开将event_type
表与player_events
表连接在一起,并以某种方式在player_events.player_id
和player_events.event_type_name
上分组,但是我无法player_events.event_type_name
正常工作。
与此类似的东西错过了零:
select player_id, event_type_name, count(event_type_name) as player_event_count from player_events group by player_id, event_type_name
最好的事情是如何做到的?
如果您有PLAYER
表( PLAYER_EVENT_TYPES
父表,其中PLAYER_ID
是主键),那么这样做的一种好方法是:
select p.player_id, et.event_type_name, count(*) as player_event_count
from event_type et
cross join players p
left join player_events pe on pe.player_id = p.player_id and pe.event_type_name = et.event_type_name
group by p.player_id, et.event_type_name;
PLAYER
表进行更新... 由于您没有PLAYER
表,因此可以这样做:
SELECT pe.player_id,
et.event_type_name,
COUNT (CASE WHEN pe.event_type_name = et.event_type_name THEN 1 ELSE NULL END) cnt
FROM player_events pe CROSS JOIN event_types et
GROUP BY pe.player_id, et.event_type_name
ORDER BY pe.player_id, et.event_type_name
(对不起,我不知道“ HIVE”,您将不得不在该数据库中使用等效的CASE
。)
我更喜欢拥有PLAYER
桌子,因为(对我而言)这还不清楚。
实际上,我更喜欢90年代早期的样式-在CASE WHEN子句可用之前:
这里的技巧是在一个(临时)表中获取所有可能的player_id
-s,在另一个表中获取所有event_type_name
-s,并将CROSS JOIN一起使用,最后将LEFT JOIN player_events
到player_events
表中。 然后计算来自player_events
表的event_type_name
-s。 event_type_name
的NULL出现根本不算在内。
如果您可以依赖于event_type_name
中存在所有player_id
-s和所有event_type_name
-s的player_events
,请执行以下操作:
WITH
-- input data for player_events, don't use in query
player_events(player_id,tm,event_type_name) AS (
SELECT 0,TIME '00:01:01','LoginEvent'
UNION ALL SELECT 1,TIME '00:01:12','LoginEvent'
UNION ALL SELECT 1,TIME '00:01:12','LoginEvent'
UNION ALL SELECT 1,TIME '00:01:12','LoginEvent'
UNION ALL SELECT 1,TIME '00:01:12','LoginEvent'
UNION ALL SELECT 1,TIME '00:01:12','LoginEvent'
UNION ALL SELECT 1,TIME '00:01:12','ProfileChangedEvent'
)
-- real query starts here - replace the comma below with WITH
,
-- all distinct player_id-s from player_events
players AS (
SELECT DISTINCT player_id FROM player_events
)
,
-- all distinct event_type_name-s from player_events
all_event_types AS (
SELECT DISTINCT event_type_name FROM player_events
)
SELECT
p.player_id
, a.event_type_name
, COUNT(e.event_type_name) AS player_event_count
FROM players p
CROSS JOIN all_event_types a
LEFT JOIN player_events e USING(player_id,event_type_name)
GROUP BY
p.player_id
, a.event_type_name
ORDER BY
p.player_id
;
如果在player_events
表中没有任何条目的player_id
-s或event_type_name
-s,则必须为player_id
-s和event_type_name
-s创建一个SELECT,确保它们返回所有可能的出现,并替换SELECT DISTINCT -s我用那些。
祝好运 -
马尔科
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.