【问题标题】:SQL left join query runs VERY slowSQL左连接查询运行非常慢
【发布时间】:2010-12-07 05:38:35
【问题描述】:

基本上,我正在尝试从数据库中提取用户尚未响应的随机民意调查问题。这个查询大约需要 10-20 秒来执行,这显然是不行的!响应表大约有 30K 行,数据库也有大约 300 个问题。

SELECT  questions.id
FROM  questions
LEFT JOIN  responses ON ( questions.id = responses.questionID
AND responses.username =  'someuser' ) 
WHERE
responses.username IS NULL 
ORDER BY RAND() ASC 
LIMIT 1

问题和响应表的 PK 是“id”,如果这很重要的话。

任何建议将不胜感激。

【问题讨论】:

    标签: sql mysql optimization


    【解决方案1】:

    问题可能不在于连接,几乎可以肯定是按顺序 rand() 对 30k 行进行排序

    【讨论】:

    • 感谢您的建议。我将按 rand() 问题调查订单。制作 questionID 和 username 列索引就可以了。希望在查询之外进行随机化处理会使其更快。
    • ORDER BY 是一种必要的邪恶——实际上没有除了排序结果之外还有其他选择。但常见的错误是在普通视图和内联视图中都使用 ORDER BY 语句 - 除非您有充分的理由,否则为最外层的查询定义 ORDER BY。
    【解决方案2】:

    见:Do not order by rand

    他建议(用您的查询替换此示例中的引号)

    SELECT COUNT(*) AS cnt FROM quotes
    
    -- generate random number between 0 and cnt-1 in your programming language and run 
    -- the query:
    
    SELECT quote FROM quotes LIMIT $generated_number, 1
    

    当然,您可以将第一条语句设置为第二条中的子选择。

    【讨论】:

    • 我怀疑随机排序 300 个问题有那么慢。
    • 随机排序一个 300 行表连接到一个 30,000 行表很可能,但是。
    【解决方案3】:

    你很可能需要一个索引

    responses.questionID
    responses.username 
    

    如果没有索引,搜索 30k 行总是很慢。

    【讨论】:

    • 这解决了我的主要问题。看起来我也需要解决“按rand()排序”。谢谢!
    • @Chaos:我在 nickf 的回答中回复了你的评论。
    【解决方案4】:

    这里有一种不同的查询方法,可能会更快:

    SELECT q.id
    FROM questions q
    WHERE q.id NOT IN (
        SELECT r.questionID
        FROM responses r
        WHERE r.username = 'someuser'
    )
    

    确保r.username 上有一个索引,这应该很快。

    以上将返回所有未回答的问题。要选择随机的,您可以使用低效(但简单)ORDER BY RAND() LIMIT 1,或使用 Tom Leys 建议的方法。

    【讨论】:

    • 你真的是说这会比加入更快吗?
    • @ChaosPandion: LEFT JOIN/IS NULL and NOT IN 是最快的(和等效的)选项explainextended.com/2009/09/18/…
    • @rexem:很有趣,在这种情况下我会使用左连接,因为我认为它看起来更好。
    • @Chaos:真的吗?我发现NOT IN 更能描述您实际尝试做的事情。也许只有我一个人,但我可以阅读NOT IN 版本,就像一个句子“获取'someuser'没有回应的问题的ID”。我不能用 LEFT JOIN 做同样的事情。
    • @unknown:只有在 SELECT 子句中使用 SELECT 时才会出现这种情况。阅读链接,注意我所说的关于 LEFT JOIN/IS NULL 如何对 MySQL 有效...
    【解决方案5】:

    OP 是否确定原始查询返回正确的结果集?

    我假设“ANDresponses.username = 'someuser'”子句被添加到加入规范中,目的是加入后将只为 someuser 没有回答的 id 生成空的右侧列。

    我的问题:该连接不会为所有用户未回答的每个 question.id 生成空右侧列吗?左连接的工作原理是,“如果目标表中的任何行与连接表达式不匹配,则为 SELECT 列列表中对目标表的所有列引用生成 NULL 值。”

    无论如何,nickf 的建议对我来说看起来不错。

    【讨论】:

    • @Herbert:当我阅读 LEFT JOIN/IS NULL 评估时,您已经找到了我畏缩的确切原因 :) 但 MySQL 是我所知道的唯一一个将其视为等同于不在:explainextended.com/2009/09/18/…
    • @rexem -- 哦,谢谢。我知道在左连接规范中包含那些非等连接条件会导致 ANSI SQL 出现问题,因为操作与用户期望的不同。你是说 MySQL 通过放弃左连接的 ANSI SQL 规范避免了这个问题?
    • @Herbert:我只知道 MySQL 优化器就是这样处理的。每个数据库都有其怪癖...
    • @rexem -- 也许我们在谈论不同的事情。问题指出与优化无关。这与用户误解 SQL 以及在连接子句中添加单独条件的工作原理有关。 ANSI SQL 规范要求的结果是不直观的,与许多用户的期望不同。除非 OP 确认原始查询的结果是正确的,否则我怀疑 join 不仅会导致优化问题,还会导致结果集本身的准确性出现问题。
    猜你喜欢
    • 2016-09-05
    • 2021-05-16
    • 1970-01-01
    • 1970-01-01
    • 2015-04-19
    • 2012-10-23
    • 1970-01-01
    • 1970-01-01
    • 2020-02-04
    相关资源
    最近更新 更多