【问题标题】:Need help understanding the SQL explanation of a JOIN query versus a query with subselects需要帮助理解 JOIN 查询与带有子选择的查询的 SQL 解释
【发布时间】:2011-06-09 05:22:03
【问题描述】:

我在这里发布了一个先前的问题,询问什么更好,JOIN 查询或使用子选择的查询。链接:Queries within queries: Is there a better way?

这是对该问题的扩展。有人可以向我解释为什么我会看到我在这里看到的吗?

查询(子选择):

SELECT article_seq, title, synopsis, body, lastmodified_date, (SELECT type_id FROM types WHERE kbarticles.type = type_seq), status, scope, images, archived, author, owner, (SELECT owner_description FROM owners WHERE kbarticles.owner = owner_seq),  (SELECT review_date FROM kbreview WHERE kbarticles.article_seq = article_seq) FROM kbarticles WHERE article_seq = $1

解释分析(子选择)

QUERY PLAN

Index Scan using article_seq_pkey on kbarticles  (cost=0.00..32.24 rows=1 width=1241) (actual time=1.421..1.426 rows=1 loops=1)

  Index Cond: (article_seq = 1511)

  SubPlan

    ->  Seq Scan on kbreview  (cost=0.00..14.54 rows=1 width=8) (actual time=0.243..1.158 rows=1 loops=1)

          Filter: ($2 = article_seq)

    ->  Seq Scan on owners  (cost=0.00..1.16 rows=1 width=24) (actual time=0.073..0.078 rows=1 loops=1)

          Filter: ($1 = owner_seq)

    ->  Index Scan using types_type_seq_key on types  (cost=0.00..8.27 rows=1 width=24) (actual time=0.044..0.050 rows=1 loops=1)

          Index Cond: ($0 = type_seq)

Total runtime: 2.051 ms

查询 (JOINs)

SELECT k.article_seq, k.title, k.synopsis, k.body, k.lastmodified_date, t.type_id, k.status, k.scope, k.images, k.archived, k.author, k.owner, o.owner_description, r.review_date FROM kbarticles k JOIN types t ON k.type = t.type_seq JOIN owners o ON k.owner = o.owner_seq JOIN kbreview r ON k.article_seq = r.article_seq WHERE k.article_seq = $1

解释分析 (JOINs)

QUERY PLAN

Nested Loop  (cost=0.00..32.39 rows=1 width=1293) (actual time=0.532..1.467 rows=1 loops=1)

  Join Filter: (k.owner = o.owner_seq)

  ->  Nested Loop  (cost=0.00..31.10 rows=1 width=1269) (actual time=0.419..1.345 rows=1 loops=1)

        ->  Nested Loop  (cost=0.00..22.82 rows=1 width=1249) (actual time=0.361..1.277 rows=1 loops=1)

              ->  Index Scan using article_seq_pkey on kbarticles k  (cost=0.00..8.27 rows=1 width=1241) (actual time=0.065..0.071 rows=1 loops=1)

                    Index Cond: (article_seq = 1511)

              ->  Seq Scan on kbreview r  (cost=0.00..14.54 rows=1 width=12) (actual time=0.267..1.175 rows=1 loops=1)

                    Filter: (r.article_seq = 1511)

        ->  Index Scan using types_type_seq_key on types t  (cost=0.00..8.27 rows=1 width=28) (actual time=0.048..0.055 rows=1 loops=1)

              Index Cond: (t.type_seq = k.type)

  ->  Seq Scan on owners o  (cost=0.00..1.13 rows=13 width=28) (actual time=0.022..0.038 rows=13 loops=1)

Total runtime: 2.256 ms

根据我在上一个问题中给出(和接受)的答案,JOINs 应该证明有更好的结果。然而,在我所有的测试中,我看到JOINs 的结果差了几毫秒。 JOINs 似乎也充满了嵌套循环。我JOINing 的所有表都已编入索引。

我是否正在做一些我应该做的不同的事情?我有什么遗漏吗?

【问题讨论】:

  • 这些表有多少行?子查询可以在少量行上更快...您尝试过 10.000 行或更多行吗?

标签: sql postgresql join query-optimization subquery


【解决方案1】:

这些查询在逻辑上是不同的。

第一个:

SELECT  article_seq, title, synopsis, body, lastmodified_date,
        (
        SELECT  type_id
        FROM    types
        WHERE   kbarticles.type = type_seq
        ),
        status, scope, images, archived, author, owner,
        (
        SELECT  owner_description
        FROM    owners
        WHERE   kbarticles.owner = owner_seq
        ),
        (
        SELECT  review_date
        FROM    kbreview
        WHERE   kbarticles.article_seq = article_seq
        )
FROM    kbarticles
WHERE   article_seq = $1

第二个:

SELECT  k.article_seq, k.title, k.synopsis, k.body, k.lastmodified_date, t.type_id, k.status,
        k.scope, k.images, k.archived, k.author, k.owner, o.owner_description, r.review_date
FROM    kbarticles k
JOIN    types t
ON      k.type = t.type_seq
JOIN    owners o
ON      k.owner = o.owner_seq
JOIN    kbreview r
ON      k.article_seq = r.article_seq
WHERE   k.article_seq = $1

如果typesownerskbreview 中有多个记录,则第一个查询将失败,而第二个查询将从kbarticles 返回重复项。

如果kbarticle 中没有typesownerskbreviews,则第一个查询将在适当的字段中返回一个NULL,而第二个查询将忽略该记录。

如果*_seq 字段似乎是PRIMARY KEY 字段,则永远不会有重复,查询也永远不会失败;同样,如果kbarticlesFOREIGN KEY 引用约束到typesownerskbreview,则不会有缺失的行。

但是,JOIN 运算符为优化器提供了更多位置:它可以使任何表领先并使用更高级的JOIN 技术,如HASH JOINMERGE JOIN,如果您使用子查询,这些技术将不可用。

【讨论】:

  • 所以,基本上你是说我应该坚持使用JOINs,因为从长远来看,它们会带来更多好处,而且真的可以忽略几毫秒?
  • @Michael:正如我所说,这些查询在逻辑上是不同的。只有当我提到的两个条件都成立时它们才相同:*_seq 字段是 PRIMARY KEY 并且您在 kbarticles 上具有 FOREIGN KEY 约束来引用所有这些。如果他们没有(他们很可能没有,因为kbreview.article_seq 似乎不是PRIMARY KEY),那么您应该选择您想要的。每篇文章可以有超过一个评论吗?如果可以,第一次查询将失败,因为文章有多个评论。
  • kbreview.article_seq 肯定不是主键。 kbreview 表还有另一列具有唯一主键。但是,kbreview.article_seq 绝对是唯一的,绝不应该包含重复值。我注意到我在该列上创建的索引没有唯一约束,所以我现在已经修复了。
  • @Michael:一篇文章可以有多个或零个typeownerkbreview 记录吗?
  • @Michael:那么您的查询是相同的,但最好还是使用JOIN
【解决方案2】:

鉴于两个计划都在执行相同的表扫描,只是以不同的方式排列,我想说两者之间没有显着差异。下臂产生单行的“嵌套循环”与单行子选择几乎相同。

联接更通用,例如,使用标量子选择不会扩展到从任何辅助表中获取两列。

【讨论】:

    【解决方案3】:

    此表列是否已编入索引? r.article_seq

    -> kbreview r 上的 Seq Scan(成本=0.00..14.54 行=1 宽度=12) (实际时间=0.267..1.175 行=1 循环=1)

    这是花费最多时间的地方。

    【讨论】:

    • 是的,该列有一个索引。 kbreview_article_seq CREATE INDEX kbreview_article_seq ON kbreview USING btree (article_seq)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-03
    相关资源
    最近更新 更多