【问题标题】:PostgreSQL - Left Join with Bad Count OutputPostgreSQL - 带有错误计数输出的左连接
【发布时间】:2015-02-12 12:53:32
【问题描述】:

我正在尝试设置一组简单的表格来显示比赛结果 - 我有以下结构:

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、姓名、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)

现在,我知道这个错误输出的原因是因为加入了 player.id = matches.winner_id,但是,我的问题是:

是否有可能只使用 一个 左连接查询来获得这些结果?如果是这样,怎么做?如果可能,我想避免进行多次查询。

【问题讨论】:

    标签: sql postgresql aggregate-filter


    【解决方案1】:

    是的。首先,您需要了解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 条件。您不能仅仅加入获胜者并期望获得所有比赛的计数。

    【讨论】:

      【解决方案2】:

      分选解决方案:

      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
      

      【讨论】:

        【解决方案3】:

        除了 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;
        

        【讨论】:

          【解决方案4】:
          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
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2022-01-16
            • 1970-01-01
            • 2019-09-26
            • 1970-01-01
            • 1970-01-01
            • 2015-12-28
            • 2018-06-22
            相关资源
            最近更新 更多