假设我有一组项目:
可以用两种方式构建查询。首先:
SELECT *
FROM TABLE
WHERE ITEM NOT IN ('item1', 'item2', 'item3', 'item4','item5')
或者,它可以写成:
SELECT *
FROM TABLE
WHERE ITEM != 'item1'
AND ITEM != 'item2'
AND ITEM != 'item3'
AND ITEM != 'item4'
AND ITEM != 'item5'
我的问题与PostgreSQL有关。
答案 0 :(得分:39)
在PostgreSQL中,合理的列表长度通常存在相当小的差异,但IN
在概念上更清晰。很长的AND ... <> ...
列表和很长的NOT IN
列表都表现得非常糟糕,AND
比NOT IN
差得多。
在这两种情况下,如果它们足够长,您甚至可以提出问题,那么您应该对值列表进行反连接或子查询排除测试。
WITH excluded(item) AS (
VALUES('item1'), ('item2'), ('item3'), ('item4'),('item5')
)
SELECT *
FROM thetable t
WHERE NOT EXISTS(SELECT 1 FROM excluded e WHERE t.item = e.item);
或:
WITH excluded(item) AS (
VALUES('item1'), ('item2'), ('item3'), ('item4'),('item5')
)
SELECT *
FROM thetable t
LEFT OUTER JOIN excluded e ON (t.item = e.item)
WHERE e.item IS NULL;
(在现代Pg版本中,无论如何都会生成相同的查询计划。)
如果值列表足够长(成千上万个项目),那么查询解析可能会开始产生很大的成本。此时,您应该考虑创建一个TEMPORARY
表,COPY
要排除到其中的数据,可能在其上创建索引,然后在临时表而不是CTE上使用上述方法之一
演示:
CREATE UNLOGGED TABLE exclude_test(id integer primary key);
INSERT INTO exclude_test(id) SELECT generate_series(1,50000);
CREATE TABLE exclude AS SELECT x AS item FROM generate_series(1,40000,4) x;
其中exclude
是要省略的值列表。
然后我将相同数据的以下方法与所有结果进行比较,以毫秒为单位:
NOT IN
列表: 3424.596 AND ...
列表: 80173.823 VALUES
基于JOIN
排除: 20.727 VALUES
的子查询排除: 20.495 JOIN
,前列表中没有索引: 25.183 ...使基于CTE的方法比AND
列表快三千倍,比NOT IN
列表快130倍。
代码在这里:https://gist.github.com/ringerc/5755247(保护你的眼睛,你们这些链接的人是谁)。
对于此数据集大小,在排除列表上添加索引没有任何区别。
注意:
IN
生成的SELECT 'IN (' || string_agg(item::text, ',' ORDER BY item) || ')' from exclude;
列表
AND
列表使用SELECT string_agg(item::text, ' AND item <> ') from exclude;
)NOT IN
翻译为<> ALL
所以......你可以看到IN
和AND
列表之间存在真正的巨大的差距与正确的联接。让我感到惊讶的是,使用VALUES
列表进行CTE的速度有多快...解析VALUES
列表几乎没有时间,执行相同或略快于大多数测试中的表格方法。
如果PostgreSQL可以自动识别一个荒谬的长IN
子句或类似AND
条件的链并转向更智能的方法,例如进行散列连接或隐式将其转换为CTE,那就太好了节点。现在它不知道该怎么做。
另见:
答案 1 :(得分:8)
我对@Jayram原来接受的答案有点不同意。
至少,该链接适用于SQL Server,并且与许多其他文章和答案相矛盾。此外,样本表上没有索引。
通常,对于子查询SQL构造
<>
(或!=
)是标量比较NOT IN
是左反半连接关系运算符简单来说
NOT IN
成为一种可以使用索引的JOIN形式(PostgreSQL除外!)!=
通常是非SARGable且不能使用索引这在dba.se上进行了讨论:"The use of NOT logic in relation to indexes"。对于PostgreSQL,那么这个explainextended article解释了内部更多(但不幸的是没有关于NOT IN的常量列表)。
无论哪种方式,对于常量列表,我都会在NOT IN
之前使用<>
,因为它更易于阅读,因为@CraigRinger解释了这一点。
对于子查询,NOT EXISTS
是要走的路