[英]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.