【问题标题】:Advanced indexing involving OR-ed conditions (pgsql)涉及 OR-ed 条件的高级索引 (pgsql)
【发布时间】:2009-11-05 00:05:01
【问题描述】:

我开始更好地掌握 PostgreSQL 索引,但我遇到了 OR 条件的问题,我不知道如何优化我的索引以获得更快的查询。

我有 6 个条件,单独运行时,成本似乎很小。以下是修剪后查询的示例,包括查询计划计算时间。

(注意:为了降低复杂性,我没有在下面输出这些查询的实际查询计划,但它们都使用 nested loop left joinsindex scans,正如我所期望的正确索引。如有必要,我可以包含查询计划以获得更有意义的响应。)

EXPLAIN ANALYZE SELECT t1.*, t2.*, t3.*
  FROM t1 LEFT JOIN t2 on t2.id = t1.t2_id LEFT JOIN t3 ON t3.id = t1.t3_id
 WHERE (conditions1)
 LIMIT 10;

QUERY PLAN
-------------------------------------------------------------------------------------
Limit  (cost=0.25..46.69 rows=1 width=171) (actual time=0.031..0.031 rows=0 loops=1)

EXPLAIN ANALYZE SELECT t1.*, t2.*, t3.*
  FROM t1 LEFT JOIN t2 on t2.id = t1.t2_id LEFT JOIN t3 ON t3.id = t1.t3_id
 WHERE (conditions2)
 LIMIT 10;

QUERY PLAN
-------------------------------------------------------------------------------------
Limit  (cost=0.76..18.97 rows=1 width=171) (actual time=14.764..14.764 rows=0 loops=1)

/* snip */

EXPLAIN ANALYZE SELECT t1.*, t2.*, t3.*
  FROM t1 LEFT JOIN t2 on t2.id = t1.t2_id LEFT JOIN t3 ON t3.id = t1.t3_id
 WHERE (conditions6)
 LIMIT 10;

QUERY PLAN
-------------------------------------------------------------------------------------
Limit  (cost=0.51..24.48 rows=1 width=171) (actual time=0.252..5.332 rows=10 loops=1)

我的问题是我想将这 6 个条件与 OR 运算符连接在一起,使每个条件成为可能。我的组合查询看起来更像这样:

EXPLAIN ANALYZE SELECT t1.*, t2.*, t3.*
  FROM t1 LEFT JOIN t2 on t2.id = t1.t2_id LEFT JOIN t3 ON t3.id = t1.t3_id
 WHERE (conditions1 OR conditions2 OR conditions3 OR conditions4 OR conditions5 OR conditions 6)
 LIMIT 10;

不幸的是,这导致查询计划大幅增加,似乎不再使用我的索引(相反,选择执行hash left join 而不是nested loop left join,并在以前用过index scans)。

Limit  (cost=142.62..510755.78 rows=1 width=171) (actual time=30.591..30.986 rows=10 loops=1)

关于 OR-ed 条件的索引,我有什么特别需要了解的可以改进我的最终查询的吗?

更新:如果我为每个单独的 SELECT 使用 UNION,这似乎可以加快查询速度。但是,如果我将来选择订购我的结果,这会阻止我吗?以下是我通过 UNION 加快查询速度的方法:

EXPLAIN ANALYZE
SELECT t1.*, t2.*, t3.*
  FROM t1 LEFT JOIN t2 on t2.id = t1.t2_id LEFT JOIN t3 ON t3.id = t1.t3_id
 WHERE (conditions1)
UNION
SELECT t1.*, t2.*, t3.*
  FROM t1 LEFT JOIN t2 on t2.id = t1.t2_id LEFT JOIN t3 ON t3.id = t1.t3_id
 WHERE (conditions2)
UNION
SELECT t1.*, t2.*, t3.*
  FROM t1 LEFT JOIN t2 on t2.id = t1.t2_id LEFT JOIN t3 ON t3.id = t1.t3_id
 WHERE (conditions3)
UNION
SELECT t1.*, t2.*, t3.*
  FROM t1 LEFT JOIN t2 on t2.id = t1.t2_id LEFT JOIN t3 ON t3.id = t1.t3_id
 WHERE (conditions4)
UNION
SELECT t1.*, t2.*, t3.*
  FROM t1 LEFT JOIN t2 on t2.id = t1.t2_id LEFT JOIN t3 ON t3.id = t1.t3_id
 WHERE (conditions5)
UNION
SELECT t1.*, t2.*, t3.*
  FROM t1 LEFT JOIN t2 on t2.id = t1.t2_id LEFT JOIN t3 ON t3.id = t1.t3_id
 WHERE (conditions6)
 LIMIT 10;

QUERY PLAN
-------------------------------------------------------------------------------------
Limit  (cost=219.14..219.49 rows=6 width=171) (actual time=125.579..125.653 rows=10 loops=1)

【问题讨论】:

    标签: postgresql indexing union


    【解决方案1】:

    根据条件,使用任何索引来帮助使用OR 表达式的复杂条件在逻辑上可能是不可能的。

    像 MySQL、PostgreSQL 8.0 和更早的状态在它们的docs on indexes

    请注意,查询或数据操作命令每个表最多可以使用一个索引。

    在 PostgreSQL 8.1 中,这有 changed

    但是,如果这没有帮助,您可以使用您尝试过的UNION 解决方案(这是 MySQL 用户的常见解决方案,它仍然具有每个表一个索引的限制)。

    您应该能够对UNION 查询的结果进行排序,但是您必须使用括号来指定ORDER BY 适用于UNION 的结果,而不仅仅是链中的最后一个子查询.

    (SELECT ... )
    UNION
    (SELECT ... )
    UNION
    (SELECT ... )
    ORDER BY columnname;
    

    我希望这会有所帮助;我不是 PostgreSQL 优化器方面的专家。您可以尝试搜索mailing list archives,或在IRC channel 上询问。

    【讨论】:

    • 我认为您指出的 8.1 更改仍然由查询计划程序确定,因此如果它似乎没有被使用,那么我基本上无法使用它我的或条件?看起来我需要使用 UNION,但很高兴知道如何通过使用括号来限制结果。谢谢!
    • 另外,我没有意识到查询计划中只使用了一个索引(8.1 之前的版本)。这有助于我更好地了解我应该如何创建索引,因此也感谢您指出这一点。
    【解决方案2】:

    (对不起 - 不知道如何回复回复,所以这是顶级)

    澄清一下 - PG 过去只对单个表扫描使用单个索引。如果您有一个连接三个表的查询,并且每个表都有一个有用的索引,那么使用所有三个表总是足够聪明。

    在您的特定情况下,可能发生的是您的 ORed 条件之间存在某种联系。 PostgreSQL 不知道这一点,因此最终假设它将匹配比实际更多的行。足够的行来更改您的查询计划。

    此外,您的 UNIONed 查询与单个查询并不完全相同,因为您单独限制每个小的查询,而不是使用 UNION 限制整个结果集。

    您应该可以订购 UNION 查询的结果,但您有 使用括号来指定 ORDER BY 适用于结果 联盟,不只是到最后 链中的子查询。

    这是不对的 - ORDER BY 适用于整个结果。

    HTH

    【讨论】:

    • 感谢您对此提供的额外帮助。在打开这个问题之前,我对 PostgreSQL 和索引的了解肯定比我多得多。 :)
    • 另外,要回答您的问题,您需要获得更多代表才能发布 cmets。只要像这样回答人们的问题,你就会获得声望。我认为 cmets 只需要 15 个代表,根本不是一个大障碍。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-15
    • 2014-03-17
    • 2018-10-18
    • 1970-01-01
    • 2021-09-20
    • 1970-01-01
    • 2013-08-06
    相关资源
    最近更新 更多