假设我有一组项目:
可以用两种方式构造查询.首先:
SELECT *
FROM TABLE
WHERE ITEM NOT IN ('item1', 'item2', 'item3', 'item4','item5')
Run Code Online (Sandbox Code Playgroud)
或者,它可以写成:
SELECT *
FROM TABLE
WHERE ITEM != 'item1'
AND ITEM != 'item2'
AND ITEM != 'item3'
AND ITEM != 'item4'
AND ITEM != 'item5'
Run Code Online (Sandbox Code Playgroud)
我的问题与PostgreSQL有关.
Cra*_*ger 43
在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);
Run Code Online (Sandbox Code Playgroud)
要么:
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;
Run Code Online (Sandbox Code Playgroud)
(在现代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;
Run Code Online (Sandbox Code Playgroud)
哪里exclude是要省略的值列表.
然后,我将相同数据的以下方法与所有结果进行比较,以毫秒为单位:
NOT IN清单:3424.596AND ...清单:80173.823VALUES基于JOIN排除:20.727VALUES基于子查询的排除:20.495JOIN,没有前列表索引:25.183......使基于CTE的方法比AND列表快三千倍,比列表快130倍NOT IN.
代码在这里: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节点,那就太好了.现在它不知道该怎么做.
也可以看看:
我对@Jayram最初接受的答案有点不同意.
尤其是,该链接适用于SQL Server,并且与许多其他文章和答案相矛盾.此外,样本表上没有索引.
通常,对于子查询SQL构造
<>(或!=)是标量比较NOT IN是一个左反半连接关系运算符简单来说
NOT IN 成为一种可以使用索引的JOIN形式(PostgreSQL除外!)!= 通常是非SARGable,不能使用索引This was discussed on dba.se: "The use of NOT logic in relation to indexes". For PostgreSQL, then this explainextended article explains the internal more (but not for a list of constants with NOT IN unfortunately).
Either way, for a list of constants, I'd use NOT IN before <> generally because it's easier to read and because of what @CraigRinger explained.
For a subquery, NOT EXISTS is the way to go