【问题标题】:Speeding up a sql query that uses exists加快使用存在的 sql 查询
【发布时间】:2016-03-02 03:08:20
【问题描述】:

我是 SQL 新手,我终于设法组合了一个查询,该查询返回我正在寻找的值!唯一的问题是,这运行得很慢,大概是因为存在子句。我希望添加 LIMIT 会显着加快速度,但不幸的是,我凭经验发现这不是真的。

是否有另一种方法可以编写此查询以“可接受”的速度返回相同的值?我意识到“可接受”是主观的,但我只是在寻找新的方法。

这里是使用的表格:

content
---------------------------------------
id            |    Int
---------------------------------------

content_revision
---------------------------------------
id            |    Int
content_id    |    ForeignKey (content)
---------------------------------------

这是我的慢查询:

SELECT outer_content.id AS "TEN_MOST_RECENT_CONTENTS_WITH_ONE_REVISION" 
    FROM content outer_content 
    JOIN content_revision ON outer_content.id = content_revision.content_id 
    WHERE EXISTS (
        SELECT inner_content.id 
            FROM content_revision 
            JOIN content inner_content ON content_revision.content_id = inner_content.id 
            WHERE outer_content.id = inner_content.id 
            GROUP BY inner_content.id HAVING count(*) = 1
    ) 
ORDER BY ts_created DESC 
LIMIT 10;

我有兴趣找到“TEN_MOST_RECENT_CONTENTS_WITH_ONE_REVISION”

【问题讨论】:

  • 生成一个执行计划并查看最昂贵的操作(一个好的 DBA 可以提供极大的帮助)。 ts_created 上的索引可能会有所帮助。请注意,LIMIT 对实际查询性能没有帮助,因为它可能需要扫描整个表才能找到“前 10”个结果。
  • 你不能用SELECT inner_content.id FROM content_revision JOIN content inner_content ON content_revision.content_id = inner_content.id GROUP BY inner_content.id HAVING count(*) = 1 ORDER BY ts_created DESC LIMIT 10;
  • @artm - 我希望能够做这样的事情,不幸的是这会导致错误......因为我按inner_content.id分组,我不能按ts_created订购,因为那里可能是用不同的 ts_created 组合在一起的多个列。按两列分组将撤消 GROUP BY 语句,因为 ts_created 值基本上都是唯一的。 :(
  • @RobertKotcher 将max(ts_created) 添加到选择列表和order by max(ts_created) desc

标签: sql performance postgresql optimization


【解决方案1】:

你真的需要内部选择吗?您可以使用:

 SELECT inner_content.id, MAX(ts_created) 
            FROM content_revision 
            JOIN content inner_content ON content_revision.content_id = inner_content.id 
            GROUP BY inner_content.id HAVING count(*) = 1
            ORDER BY MAX(ts_created) DESC 
            LIMIT 10;

由于您使用的是 group by,如果您想通过ts_created 订购,您可以使用MAX(ts_created)。由于HAVING

,反正只有一条记录

【讨论】:

  • 这似乎有效!让我再玩几分钟,如果它正在做我期望的事情,我会投票。谢谢!
  • 您需要在此处检查的一件事是行的顺序,MAX(ts_created) 将始终评估为一个日期,它可能会与您期望的结果相混淆。
  • @JorgeCampos MAX(ts_created) 将在 content_revision 中返回每个结果的最大日期。因此它不会返回单个日期,而是每个内容的 MAX 日期,因此 order by 仍然有效。
  • @RobertKotcher 出于好奇,这对速度提高了多少?
  • @artm 数量级!!我的原始查询:(cost=2400404.10..2400411.97 rows=3146 width=12) 您的查询:(cost=1131.91..1131.93 rows=10 width=12)。您的解决方案非常有见地
【解决方案2】:

您根本不需要使用WHERE EXISTS 条件。你所要做的就是GROUP BY content.id, content.ts_created并添加HAVING count(content_revision.*) = 1,然后限制,订单等......

-- get ten most recent contents with one revision
SELECT content.id, content.ts_created 
FROM content
JOIN content_revision ON content.id = content_revision.content_id 
GROUP BY content.id, content.ts_created 
HAVING count(content_revision.*) = 1
ORDER BY content.ts_created DESC 
LIMIT 10;

【讨论】:

  • 这个答案是错误的。你会得到column "content.ts_created" must appear in the GROUP BY clause or be used in an aggregate function这不是你可以做这样事情的MySql
  • @JorgeCampos 你是对的。我错过了。我修复了查询。
  • 这可能不会给出 op 想要的结果,因为它将按 ID 和 TS_CREATED 分组,而他只想按 ID 分组。
  • 这仍然不正确 - GROUP BY X, Y 表示将为包含 both X & Y 的所有行集生成一行。
  • 好吧,我必须同意你的观点,我假设 ts_created 来自content_revision,如果不是你一直都是对的。很抱歉浪费了我们俩的时间……
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-30
  • 2013-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多