如何在一个表中查找在另一表中没有对应行的行


73

我在两个表之间有1:1的关系。我想查找表A中所有没有在表B中对应的行。我使用以下查询:

SELECT id 
  FROM tableA 
 WHERE id NOT IN (SELECT id 
                    FROM tableB) 
ORDER BY id desc

id是两个表中的主键。除了主键索引外,我还在tableA(id desc)上有一个索引。

使用H2(Java嵌入式数据库),将对tableB进行全表扫描。我想避免全表扫描。

如何重写此查询以快速运行?我应该怎样索引?


1
每次您写“ WHERE col [NOT] IN(从其他表中选择SELECT col)”时,最好使用[NOT] EXISTS进行重构。
topchef

Answers:


95
select tableA.id from tableA left outer join tableB on (tableA.id = tableB.id)
where tableB.id is null
order by tableA.id desc 

如果您的数据库知道如何做索引交集,则只会触摸主键索引


11
这就是为什么我喜欢Stack Overflow。星期六,SQL问题-在5分钟内准确,成功地回答了问题!
Steve McLeod

2
您在其他答案中也得到了一些好的建议。自然地,我认为我的速度最快:-),但是数据库实现的差异很大,而且我对H2没有经验。如果您对不同的方法进行基准测试并用结果更新问题,那就太好了。
SquareCog

33

您也可以使用exists,因为有时它比更快left join。您必须对它们进行基准测试,以确定要使用哪一个。

select
    id
from
    tableA a
where
    not exists
    (select 1 from tableB b where b.id = a.id)

为了证明它exists比效率更高left join,这是SQL Server 2008中这些查询的执行计划:

left join -子树总成本:1.09724:

左联接

exists -子树总成本:1.07421:

存在


1
+1:如果子查询(在这种情况下是相关的)返回至少一行,则认为EXISTS条件“满足”。
OMG Ponies,2009年

基准测试是一个好主意。我努力地想弄清楚一个数据库在存在+相关子查询的幕后可以做什么,这将使其比仅索引哈希联接更快。你知道吗?
SquareCog

2
Exists没有使用您的标准相关子查询。它使用半联接。SQL Server 2008上的执行计划是对left join索引进行两次索引扫描以对哈希进行匹配,对筛选进行筛选以进行选择。对于not exists,它是两次索引扫描,以对与select-no过滤器的哈希匹配。该exists哈希匹配实际上要稍微比快 left joinleft join具有1.09的总成本,not exists在1.07DimCustomerAdventureWorksDWAdventureWorksDW2008
埃里克

不错!谢谢。那是一个聪明的优化器。授予的成本是近似的,但我是根据过滤器vs半联接原理购买的。
SquareCog

7

您必须对照表B中的每个ID检查表A中的每个ID。一个功能完备的RDBMS(例如Oracle)将能够将其优化为INDEX FULL FAST SCAN,而根本不用碰桌子。我不知道H2的优化器是否如此智能。

H2确实支持MINUS语法,因此您应该尝试一下

select id from tableA
minus
select id from tableB
order by id desc

可能执行得更快;当然值得进行基准测试。


5

对于我的小型数据集,Oracle几乎为所有这些查询提供了使用主键索引的完全相同的计划,而无需涉及表。MINUS版本是个例外,尽管计划成本较高,但MINUS版本仍能执行较少的一致性获取。

--Create Sample Data.
d r o p table tableA;
d r o p table tableB;

create table tableA as (
   select rownum-1 ID, chr(rownum-1+70) bb, chr(rownum-1+100) cc 
      from dual connect by rownum<=4
);

create table tableB as (
   select rownum ID, chr(rownum+70) data1, chr(rownum+100) cc from dual
   UNION ALL
   select rownum+2 ID, chr(rownum+70) data1, chr(rownum+100) cc 
      from dual connect by rownum<=3
);

a l t e r table tableA Add Primary Key (ID);
a l t e r table tableB Add Primary Key (ID);

--View Tables.
select * from tableA;
select * from tableB;

--Find all rows in tableA that don't have a corresponding row in tableB.

--Method 1.
SELECT id FROM tableA WHERE id NOT IN (SELECT id FROM tableB) ORDER BY id DESC;

--Method 2.
SELECT tableA.id FROM tableA LEFT JOIN tableB ON (tableA.id = tableB.id)
WHERE tableB.id IS NULL ORDER BY tableA.id DESC;

--Method 3.
SELECT id FROM tableA a WHERE NOT EXISTS (SELECT 1 FROM tableB b WHERE b.id = a.id) 
   ORDER BY id DESC;

--Method 4.
SELECT id FROM tableA
MINUS
SELECT id FROM tableB ORDER BY id DESC;

1
对于编写它d r o p,它使人们阅读代码,您得到+1
Flavius 2015年


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.