【问题标题】:JOIN after processing SELECT处理 SELECT 后加入
【发布时间】:2009-11-05 18:43:06
【问题描述】:

给定以下架构:

CREATE TABLE players (
  id BIGINT PRIMARY KEY,
  name TEXT UNIQUE
);

CREATE TABLE trials (
  timestamp TIMESTAMP PRIMARY KEY,
  player BIGINT,
  score NUMERIC
);

我如何创建一个SELECT,它首先从trials 中找到最好的分数,然后从users 中加入name 字段?使用此查询后,我已经能够获得我的分数:

SELECT * FROM trials GROUP BY player ORDER BY score ASC LIMIT 10;

我返回前 10 名分数的查询如下所示:

CREATE VIEW top10place AS
  SELECT player.name AS name, trial.*
    FROM trials AS trial, players AS player
    WHERE trial.player = player.id
      AND trial.score = (
        SELECT MAX(score)
        FROM trials AS tsub
        WHERE tsub.player = trial.player
      )
    ORDER BY trial.score DESC, trial.timestamp ASC
    LIMIT 10;

但是当我在表中点击数千个条目时,数据库性能开始下降。我认为子查询正在扼杀我的表现。第一个查询(仅返回最高分)仍然执行得很好,所以我想知道是否有办法在选择最高分后强制执行JOIN 操作。

编辑请注意,查询将返回排名前 10 的玩家,而不仅仅是前 10 名的分数。如果同一个玩家有很多高分,他应该只在前10名中出现一次。

我使用的是 SQLite,所以它没有 SQL Server 或 MySQL 的一些扩展功能。

【问题讨论】:

    标签: sql optimization sqlite join


    【解决方案1】:

    没有运行sqlite,希望限制是对的。

    select players.name, trials.player, trials.timestamp, trials.score from
        (select player, score, timestamp from
        trials order by score desc, timestamp asc limit 10) trials, players
    where players.id = trials.player
    

    问候

    【讨论】:

    • 做到了...使用这种方法在
    【解决方案2】:

    这是你做某事比它需要的更难的一个例子。正确的代码是:

    CREATE VIEW top10place AS
      SELECT player.name AS name, trial.*
        FROM trials AS trial, players AS player
        WHERE trial.player = player.id
        ORDER BY trial.score ASC, trial.timestamp ASC
        LIMIT 10;
    

    基本上,让 LIMIT 语句完成工作:)

    【讨论】:

    • 不应该是“ORDER BY trial.score DESC”吗?
    • 这种方法的问题是,如果同一个玩家必须进入前十名,他们都会出现。我正在寻找排名靠前的玩家,因此只有在此列表中的唯一玩家条目。
    • @Andomar 是的,那是我的错……我原来的问题有错别字。
    【解决方案3】:

    如果优化器为每一行运行 WHERE 中的子查询,它的开销可能会很大。

    (编辑)这是编写查询的另一种方式,现在使用排他连接:它表示该用户没有得分更高的行:

    SELECT 
         (select name from user where id = cur.userid) as UserName
    ,    cur.score as MaxScore
    FROM trails cur
    LEFT JOIN trials higher
        ON higher.userid = cur.userid
        AND higher.timestamp <> cur.timestamp
        AND higher.score > cur.score
    WHERE higher.userid is null
    ORDER BY cur.score DESC
    LIMIT 10
    

    这将返回 10 个得分最高的用户。如果您想获得 10 个最高分(不考虑用户),请查看 Silas 的答案。

    【讨论】:

    • 这种方法在我的数据库上也很慢,有 10,000 名玩家和 100,000 次试验。需要几分钟才能完成。你能提供另一种可能性吗?
    • @jheddings:编辑为另一种编写查询的方式
    【解决方案4】:

    如前所述,由于您在播放器和试验之间的识别键是 player.id 和 trial.player,因此您应该在 trial.player 上有一个索引。特别是如果您经常关联这两个表。

    您也可以尝试使您的查询更像。

    SELECT p.name as name, t.* FROM players as p
    INNER JOIN (SELECT * FROM trials WHERE trials.score = (SELECT MAX(score) FROM trials as tsub WHERE tsub.player = trials.player) LIMIT 10) as t ON t.player = p.id
    ORDER BY t.score DESC, t.timestamp ASC
    

    这甚至可以再优化一点,但是如果没有一些数据可以引发查询,我就不擅长。

    【讨论】:

      猜你喜欢
      • 2019-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多