【发布时间】:2024-04-21 02:00:02
【问题描述】:
我遇到了一些与我有些相关的问题(例如1、2)。但他们都没有真正回答这个问题。
基本上我的问题是 Postgres 拒绝在某些看似简单的查询中使用索引。我设法将我的复杂查询归结为以下一些示例:
-- SETUP
CREATE TEMP TABLE test (
id BIGSERIAL PRIMARY KEY,
DATA INT
);
INSERT INTO test(DATA)
SELECT RANDOM() FROM GENERATE_SERIES(1, 100000);
-- just in case the stats are outdated
VACUUM ANALYSE test;
-- QUERIES
-- correct but does a full table scan
EXPLAIN SELECT
id,
DATA
FROM
test
WHERE
(FALSE
OR id IN (
SELECT
UNNEST(ARRAY[100])
));
-- simpler than above but still does a table scan
EXPLAIN SELECT
id,
DATA
FROM
test
WHERE
FALSE
OR id IN (SELECT 100);
解释的输出:
QUERY PLAN
----------------------------------------------------------
Seq Scan on test (cost=0.01..1791.01 rows=50000 width=12)
Filter: (hashed SubPlan 1)
SubPlan 1
-> Result (cost=0.00..0.01 rows=1 width=4)
-- uses PRIMARY KEY index correctly but limited to 1 ID
-- NOTE that it uses "=" instead of "IN"
EXPLAIN SELECT
id,
DATA
FROM
test
WHERE
FALSE
OR id = (SELECT 100 LIMIT 1);
QUERY PLAN
---------------------------------------------------------------------
Index Scan using test_pkey on test (cost=0.30..8.32 rows=1 width=12)
Index Cond: (id = $0)
InitPlan 1 (returns $0)
-> Limit (cost=0.00..0.01 rows=1 width=4)
-> Result (cost=0.00..0.01 rows=1 width=4)
正确使用 PRIMARY KEY 索引,但我需要 LEFT JOIN 而不是 INNER JOIN。 -- 见下一个例子。
EXPLAIN
WITH ids AS (
SELECT
UNNEST(ARRAY[100]) AS ids )
SELECT
id,
DATA
FROM
test
INNER JOIN ids ON
id = ids.ids;
QUERY PLAN
---------------------------------------------------------------------------
Nested Loop (cost=0.29..8.34 rows=1 width=12)
-> ProjectSet (cost=0.00..0.02 rows=1 width=4)
-> Result (cost=0.00..0.01 rows=1 width=0)
-> Index Scan using test_pkey on test (cost=0.29..8.31 rows=1 width=12)
Index Cond: (id = (unnest('{100}'::integer[])))
我需要一个左连接,但这会进行全表扫描
EXPLAIN WITH ids AS (
SELECT 100 AS id
)
SELECT
test.id,
DATA
FROM
test
LEFT JOIN ids ON
test.id = ids.id;
QUERY PLAN
-----------------------------------------------------------
Seq Scan on test (cost=0.00..1541.00 rows=100000 width=12)
所以我的问题:
- 为什么
FALSE OR ... IN ...阻止使用索引,而FALSE OR ... = ...没问题? - 为什么在我的示例中 INNER JOIN 使用索引而 LEFT JOIN 没有?
- 是否有某种方法可以在不使用 UNION 或复制整个查询的情况下重写第一个查询? (我的实际查询要复杂得多)
【问题讨论】:
-
您必须使用
UNION重写查询。ORis a performance killer. -
左连接不限制从表
test返回的行,因此需要该表中的所有行,而 Seq Scan 是最有效的方法。 -
@LaurenzAlbe 您发布的链接非常有帮助。它提到了一种使用 ANY 重写 OR IN 部分的更有效方法
标签: sql postgresql performance indexing sql-execution-plan