【问题标题】:Update using range over multiple primary key columns doesn't use index在多个主键列上使用范围更新不使用索引
【发布时间】:2020-11-06 03:55:48
【问题描述】:

我有一个包含两个数字列作为主键的表。在事务中,我选择第一行(按主键列排序)N 行(N=500 左右)进行更新,处理它们,然后更新它们。

SELECT ...
ORDER BY pk1, pk2
LIMIT 500
FOR UPDATE

现在,我不确定在更新的WHERE 子句中选择这些行的最佳方法是什么。我试过这个:

array[pk1, pk2] >= array[$first_pk1_value, $first_pk2_value]
AND array[pk1, pk2] <= array[$last_pk1_value, $last_pk2_value]

(其中pk1pk2 是主键列,${first,last}_pk{1,2}_value 是选择中扫描的第一行和最后一行对应列的扫描值)

鉴于数组是按字典顺序排列的,就像ORDER BY pk1, pk2 一样,这会找到正确的行。

我也尝试过类似的方法:

(pk1 = $first_pk1_value AND pk2 >= $first_pk2_value)
OR (pk1 > $first_pk1_value AND pk1 < $last_pk1_value)
OR (pk1 = $last_pk1_value AND pk2 <= $last_pk2_value)

两者都有效,但执行顺序扫描。由于 WHERE 子句仅表示主键的范围,我希望 Postgres 进行索引扫描。

是 Postgres 只是不支持在多列索引上选择范围,还是我做错了什么?

【问题讨论】:

  • 对于 postgres 来说,“in approach”可能会更好(对不起,我使用 oracle。如果也强制使用 ms-sql ;-))。就像“在(array [])中的(pk1,pk2)”stackoverflow.com/questions/6672665/…

标签: sql postgresql indexing


【解决方案1】:

更新

为了回应 Toni 在下面的评论,我尝试像在 python 中一样使用元组,它的效果比我原来的建议好得多。基于analyze 输出,隐式row(id1, id2) 与支持PK 的索引兼容。

select version();
                                                                version                                                                 
----------------------------------------------------------------------------------------------------------------------------------------
 PostgreSQL 10.12 (Ubuntu 10.12-0ubuntu0.18.04.1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0, 64-bit
(1 row)

explain analyze 
 select * from testidx_array 
  where (id1, id2) between (8, 150) and (9, 2000);

                                                             QUERY PLAN                                                              
-------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on testidx_array  (cost=423.81..1263.91 rows=19855 width=40) (actual time=1.772..4.148 rows=11851 loops=1)
   Recheck Cond: ((ROW(id1, id2) >= ROW(8, 150)) AND (ROW(id1, id2) <= ROW(9, 2000)))
   Heap Blocks: exact=54
   ->  Bitmap Index Scan on testidx_array_pkey  (cost=0.00..418.84 rows=19855 width=0) (actual time=1.722..1.722 rows=11851 loops=1)
         Index Cond: ((ROW(id1, id2) >= ROW(8, 150)) AND (ROW(id1, id2) <= ROW(9, 2000)))
 Planning time: 0.096 ms
 Execution time: 4.867 ms
(7 rows)

旧答案,以下,已被取代

您应该能够通过指定pk1 的范围然后为array[pk1, pk2] 条件包含and 来强制使用索引。

where pk1 between $first_pk1_value and $last_pk1_value
  and array[pk1, pk2] between array[$first_pk1_value, $first_pk2_value] 
                          and array[$last_pk1_value, $last_pk2_value]

这在测试表中对我有用:

\d testidx_array
            Table "public.testidx_array"
  Column  |  Type   | Collation | Nullable | Default 
----------+---------+-----------+----------+---------
 id1      | integer |           | not null | 
 id2      | integer |           | not null | 
 somedata | text    |           |          | 
Indexes:
    "testidx_array_pkey" PRIMARY KEY, btree (id1, id2)

explain analyze
 select * from testidx_array 
  where array[id1, id2] between array[8,150] and array[9,2000];

                                                    QUERY PLAN                                                     
-------------------------------------------------------------------------------------------------------------------
 Seq Scan on testidx_array  (cost=0.00..1943.00 rows=500 width=40) (actual time=42.011..50.758 rows=11851 loops=1)
   Filter: ((ARRAY[id1, id2] >= '{8,150}'::integer[]) AND (ARRAY[id1, id2] <= '{9,2000}'::integer[]))
   Rows Removed by Filter: 88149
 Planning time: 0.151 ms
 Execution time: 51.325 ms
(5 rows)

explain analyze 
 select * from testidx_array 
  where id1 between 8 and 9 
    and array[id1, id2] between array[8,150] and array[9,2000];

                                                             QUERY PLAN                                                              
-------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on testidx_array  (cost=418.85..1258.91 rows=99 width=40) (actual time=2.278..11.109 rows=11851 loops=1)
   Recheck Cond: ((id1 >= 8) AND (id1 <= 9))
   Filter: ((ARRAY[id1, id2] >= '{8,150}'::integer[]) AND (ARRAY[id1, id2] <= '{9,2000}'::integer[]))
   Rows Removed by Filter: 8149
   Heap Blocks: exact=90
   ->  Bitmap Index Scan on testidx_array_pkey  (cost=0.00..418.82 rows=19853 width=0) (actual time=2.138..2.138 rows=20000 loops=1)
         Index Cond: ((id1 >= 8) AND (id1 <= 9))
 Planning time: 0.289 ms
 Execution time: 11.693 ms
(9 rows)



【讨论】:

  • 成功了,谢谢!但我想了解为什么 Postgres 不能自己解决这个问题。是否有我失踪的根本原因,或者它不够聪明(还)?
  • @ToniCárdenas 哇!请看我的更新。看来有办法了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-20
  • 1970-01-01
  • 2013-08-02
  • 1970-01-01
  • 2015-10-19
  • 1970-01-01
  • 2020-05-20
相关资源
最近更新 更多