繁体   English   中英

当左表没有所有按列分组时的左联接

[英]Left join when left table does not have all group by columns

我发现很难用精确的措词来表达这个问题。

因此,为简化起见:我有两个表,将它们event_typeplayer_events 第一个表是事件类型(字符串)的列表,称为列event_type.name

player_events是单个玩家事件的列表。 有些玩家可能会错过一些事件,并且同一位玩家可能会发生多次事件。 因此, player_events具有相关的列player_events.player_idplayer_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_idplayer_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_eventsplayer_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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM