PostgreSQL - 左连接错误计数输出
PostgreSQL - Left Join with Bad Count Output
我正在尝试设置一组简单的表格来显示锦标赛的结果 - 我有以下结构:
CREATE TABLE players(
id SERIAL PRIMARY KEY,
name TEXT);
CREATE TABLE matches(
id SERIAL PRIMARY KEY,
player_one_id INTEGER REFERENCES players,
player_two_id INTEGER REFERENCES players,
winner_id INTEGER REFERENCES players);
并且我输入了一些测试数据,如下:
INSERT INTO players (name) VALUES ('Mike Jones');
INSERT INTO players (name) VALUES ('Albert Awesome');
INSERT INTO players (name) VALUES ('Sad Sally');
INSERT INTO players (name) VALUES ('Lonely Lenny');
INSERT INTO matches (player_one_id, player_two_id, winner_id) VALUES (1,2,1);
INSERT INTO matches (player_one_id, player_two_id, winner_id) VALUES (3,4,4);
我正在尝试执行一个查询,该查询为每个玩家提供以下结果:
id, name, matched_won, matches_played.
到目前为止我有以下查询:
SELECT players.id, players.name, count(matches.winner_id) as matches_won
, count(matches.id) as matches_played
FROM players left join matches
ON players.id = matches.winner_id
GROUP BY players.id
ORDER BY matches_won DESC
而且,不幸的是,我得到了如下不正确的输出(每个玩家应该有 1 matches_played):
id | name | matches_won | matches_played
----+----------------+-------------+----------------
4 | Lonely Lenny | 1 | 1
1 | Mike Jones | 1 | 1
2 | Albert Awesome | 0 | 0
3 | Sad Sally | 0 | 0
(4 rows)
现在,我知道这个错误输出的原因是因为加入 players.id = matches.winner_id,但是,我的问题是:
仅一个左连接查询就可以获得这些结果吗?如果是这样,如何?如果可能,我想避免进行多次查询。
是的。首先,您需要了解 count()
只是计算具有非 NULL 值的行数,因此您的两个计数应该相同。
要获得获胜者,请使用条件聚合:
SELECT p.id, p.name,
sum(case when m.winner_id = p.id then 1 else 0 end) as matches_won,
count(m.id) as matches_played
FROM players p left join
matches m
ON p.id in (m.player_one_id, m.player_two_id)
GROUP BY p.id
ORDER BY matches_won DESC;
您还需要修复 join
条件。您不能只加入获胜者并期望获得所有比赛的计数。
分select解法:
SELECT players.id, players.name,
(select count(*)
from matches
where matches.winner_id = players.id) as matches_won,
(select count(*)
from matches
where players.id in (player_one_id, player_two_id)) as matches_played
FROM players
ORDER BY matches_won DESC
除了 Gordon 的回答之外,您还可以使用 COUNT() 而不是 SUM(),使用 NULLIF 或 FILTER(从 PostgreSQL 9.4 开始过滤):
使用 NULLIF() 因为在使用列名时 NULL 不算在内:
SELECT p.id, p.name,
count(nullif(m.winner_id <> p.id, false)) as matches_won,
count(m.id) as matches_played
FROM players p
left join matches m ON p.id in (m.player_one_id, m.player_two_id)
GROUP BY p.id
ORDER BY
matches_won DESC;
并使用过滤器:
SELECT p.id, p.name,
count(*) filter (WHERE m.winner_id = p.id) as matches_won,
count(m.id) as matches_played
FROM players p
left join matches m ON p.id in (m.player_one_id, m.player_two_id)
GROUP BY p.id
ORDER BY
matches_won DESC;
SELECT p.name,COUNT(m.player_one_id)+ COUNT(m1.player_two_id) AS num_of_matches_played
,COUNT(m2.winner_id) AS num_of_matches_won FROM players p
LEFT OUTER JOIN matches m ON p.id = m.player_one_id
LEFT OUTER JOIN matches m1 ON p.id = m1.player_two_id
LEFT OUTER JOIN matches m2 ON p.id = m2.winner_id
GROUP BY p.name
我正在尝试设置一组简单的表格来显示锦标赛的结果 - 我有以下结构:
CREATE TABLE players(
id SERIAL PRIMARY KEY,
name TEXT);
CREATE TABLE matches(
id SERIAL PRIMARY KEY,
player_one_id INTEGER REFERENCES players,
player_two_id INTEGER REFERENCES players,
winner_id INTEGER REFERENCES players);
并且我输入了一些测试数据,如下:
INSERT INTO players (name) VALUES ('Mike Jones');
INSERT INTO players (name) VALUES ('Albert Awesome');
INSERT INTO players (name) VALUES ('Sad Sally');
INSERT INTO players (name) VALUES ('Lonely Lenny');
INSERT INTO matches (player_one_id, player_two_id, winner_id) VALUES (1,2,1);
INSERT INTO matches (player_one_id, player_two_id, winner_id) VALUES (3,4,4);
我正在尝试执行一个查询,该查询为每个玩家提供以下结果:
id, name, matched_won, matches_played.
到目前为止我有以下查询:
SELECT players.id, players.name, count(matches.winner_id) as matches_won
, count(matches.id) as matches_played
FROM players left join matches
ON players.id = matches.winner_id
GROUP BY players.id
ORDER BY matches_won DESC
而且,不幸的是,我得到了如下不正确的输出(每个玩家应该有 1 matches_played):
id | name | matches_won | matches_played
----+----------------+-------------+----------------
4 | Lonely Lenny | 1 | 1
1 | Mike Jones | 1 | 1
2 | Albert Awesome | 0 | 0
3 | Sad Sally | 0 | 0
(4 rows)
现在,我知道这个错误输出的原因是因为加入 players.id = matches.winner_id,但是,我的问题是:
仅一个左连接查询就可以获得这些结果吗?如果是这样,如何?如果可能,我想避免进行多次查询。
是的。首先,您需要了解 count()
只是计算具有非 NULL 值的行数,因此您的两个计数应该相同。
要获得获胜者,请使用条件聚合:
SELECT p.id, p.name,
sum(case when m.winner_id = p.id then 1 else 0 end) as matches_won,
count(m.id) as matches_played
FROM players p left join
matches m
ON p.id in (m.player_one_id, m.player_two_id)
GROUP BY p.id
ORDER BY matches_won DESC;
您还需要修复 join
条件。您不能只加入获胜者并期望获得所有比赛的计数。
分select解法:
SELECT players.id, players.name,
(select count(*)
from matches
where matches.winner_id = players.id) as matches_won,
(select count(*)
from matches
where players.id in (player_one_id, player_two_id)) as matches_played
FROM players
ORDER BY matches_won DESC
除了 Gordon 的回答之外,您还可以使用 COUNT() 而不是 SUM(),使用 NULLIF 或 FILTER(从 PostgreSQL 9.4 开始过滤):
使用 NULLIF() 因为在使用列名时 NULL 不算在内:
SELECT p.id, p.name,
count(nullif(m.winner_id <> p.id, false)) as matches_won,
count(m.id) as matches_played
FROM players p
left join matches m ON p.id in (m.player_one_id, m.player_two_id)
GROUP BY p.id
ORDER BY
matches_won DESC;
并使用过滤器:
SELECT p.id, p.name,
count(*) filter (WHERE m.winner_id = p.id) as matches_won,
count(m.id) as matches_played
FROM players p
left join matches m ON p.id in (m.player_one_id, m.player_two_id)
GROUP BY p.id
ORDER BY
matches_won DESC;
SELECT p.name,COUNT(m.player_one_id)+ COUNT(m1.player_two_id) AS num_of_matches_played
,COUNT(m2.winner_id) AS num_of_matches_won FROM players p
LEFT OUTER JOIN matches m ON p.id = m.player_one_id
LEFT OUTER JOIN matches m1 ON p.id = m1.player_two_id
LEFT OUTER JOIN matches m2 ON p.id = m2.winner_id
GROUP BY p.name