当前位置: 首页 > 面试题库 >

将同一表的多个列上的条件计数相加

卜瀚漠
2023-03-14
问题内容

我正在寻找一种“更好”的方式来执行查询,在该查询中,我想向单个玩家显示他之前玩过的游戏以及与每个此类对手相关的获胜记录。

以下是涉及的表格,精简如下:

create table player (player_id int, username text);
create table match (winner_id int, loser_id int);

insert into player values (1, 'john'), (2, 'mary'), (3, 'bob'), (4, 'alice');
insert into match values (1, 2), (1, 2), (1, 3), (1, 4), (1, 4), (1, 4)
                       , (2, 1), (4, 1), (4, 1);

因此,约翰对玛丽的战绩是2胜1负。vs鲍勃1胜0负; 和爱丽丝的3胜2负。

create index idx_winners on match(winner_id);
create index idx_winners on match(loser_id);

我正在使用Postgres 9.4。我脑海中有些东西告诉我要考虑一下,LATERAL但是我很难理解这样的“形状”。

以下是我当前正在使用的查询,但是有些“感觉不到”。请帮助我学习和改进。

select p.username as opponent, 
       coalesce(r.won, 0) as won, 
       coalesce(r.lost, 0) as lost
from (
    select m.winner_id, m.loser_id, count(m.*) as won, (
        select t.lost
        from (
            select winner_id, loser_id, count(*) as lost
            from match
            where loser_id = m.winner_id
            and winner_id = m.loser_id
            group by winner_id, loser_id
        ) t 
    )   
    from match m
    where m.winner_id = 1   -- this would be a parameter
    group by m.winner_id, m.loser_id
) r 
join player p on p.player_id = r.loser_id;

这按预期工作。只是想学习一些技巧或更好但更合适的技术来做到这一点。

opponent  won  lost
--------  ---  ----
alice     3    2
bob       1    0
mary      2    1

问题答案:

询问

该查询并不像乍看起来那样简单。最短的查询字符串不一定会产生最佳性能。这应该 尽可能快 ,为此应尽可能短:

SELECT p.username, COALESCE(w.ct, 0) AS won, COALESCE(l.ct, 0) AS lost
FROM  (
   SELECT loser_id AS player_id, count(*) AS ct
   FROM   match
   WHERE  winner_id = 1  -- your player_id here
   GROUP  BY 1           -- positional reference (not your player_id)
   ) w
FULL JOIN (
   SELECT winner_id AS player_id, count(*) AS ct
   FROM   match
   WHERE  loser_id = 1   -- your player_id here
   GROUP  BY 1
   ) l USING (player_id)
JOIN   player p USING (player_id)
ORDER  BY 1;

结果完全符合要求:

username | won | lost
---------+-----+-----
alice    | 3   | 2
bob      | 1   | 0
mary     | 2   | 1

SQL Fiddle- 具有更多显示的测试数据

关键功能是 FULL [OUTER] JOIN
两个子查询之间的输赢。这将产生一个表格,其中列出了我们的候选人与之对抗的所有玩家。USING连接条件中的子句可以方便地将两player_id列合并为
一个

在那之后,用一个JOINplayer获取名称,并将
COALESCE
NULL替换为0。Voil谩。

指数

使用两个多列 索引 甚至会更快:

CREATE INDEX idx_winner on match (winner_id, loser_id);
CREATE INDEX idx_loser  on match (loser_id, winner_id);

当您从中获得仅索引扫描时。然后Postgres甚至根本不访问match 您将获得超快速的结果。

使用两integer列,您碰巧达到了 局部最优值 :这些索引的大小与简单索引的大小相同。细节:

  • 复合索引对第一个字段的查询是否也有用?

较短但较慢

您可以运行相关的子查询,例如@Giorgi建议,只要工作
正常即可

SELECT *
FROM  (
   SELECT username
       , (SELECT count(*) FROM match
          WHERE  loser_id  = p.player_id
          AND    winner_id = 1) AS won
       , (SELECT count(*) FROM match
          WHERE  winner_id = p.player_id
          AND    loser_id  = 1) AS lost
   FROM   player p
   WHERE  player_id <> 1
   ) sub
WHERE (won > 0 OR lost > 0)
ORDER  BY username;

适用于 小型
桌子,但不能扩展。这需要playermatch每个现有播放器上进行顺序扫描,并在其上进行两次索引扫描。将效果与进行比较EXPLAIN ANALYZE



 类似资料:
  • 我有一个案例,需要找出不同版本的表的同一字段之间的差异。 例如: 我有一个名为BankAccount的表,其字段如下 编号、余额、日期等。 现在我想要实现的基本上是在这个表上针对两个不同的日期运行一个select,并找出日期1和日期2的balance字段值之间的差异。 更像:。 我知道,我可以使用其中一个作为分离条件来运行两个选择,这样我至少可以得到两个日期之间的公共记录。我的问题是发现差异和使用

  • 我想代表其他列条件以不同的顺序排列我的列。 有一张桌子看起来像这样: 如果状态为1,则按日期ASC排序,状态为0按日期DESC排序 寻找以下结果 有没有办法点这个。哪儿也找不到 谢谢

  • 我试图在一系列列表中添加第一个值,然后从一系列列表中添加第二个值,依此类推。 例如: 因此,我希望 180, 210,240, 270, 300 到目前为止,我的代码是:

  • 我为每个日期创建了多个表来存储每个日期的一些信息。例如历史3108、历史0109..所有这些表共享相同的模式。有时候,我需要查询多个表,获取记录的行数和计数。在oracle和SQL Server中,最快的方法是什么? 目前我正在这样做。。。 当我需要多个表的计数时:为每个表选择计数(*),然后添加 当我需要多个表的记录时:从表1中选择*,从表2中选择*(基本上为每个表选择*) 如果我们在一个事务中

  • 问题内容: 我是Oracle的新手。我有一个Oracle表有三列:,和。在第三列中的行具有值,或 。 我想使用count运行查询,以显示可维修的数量,正在维修的数量,针对每个项目类别的谴责数量。 我想运行类似的东西: 我无法在计数内运行内部查询。 这是我希望结果集看起来像的样子: 问题答案: 您可以在COUNT函数中使用CASE或DECODE语句。 输出:

  • 问题内容: 我正在尝试计算连续4个单独的列中的不同条目,然后总计结果。 例如,表头看起来与此类似: 每个列(保存ID)可以具有文本值W,X,Y或Z。列可以具有相同的值。 我正在尝试做的是找到一种计算列中每个条目的方法,但是每行只对W,X,Y和Z进行一次计数。因此,如果: 结果表将是: 任何帮助将不胜感激。 问题答案: 也许我错过了一些东西,但这会很简单: 没有理由一起使用,因为这样做会使结果明显。