SQL-从一个表中查找另一个表中不存在的记录


310

我有以下两个SQL表(在MySQL中):

Phone_book
+----+------+--------------+
| id | name | phone_number |
+----+------+--------------+
| 1  | John | 111111111111 |
+----+------+--------------+
| 2  | Jane | 222222222222 |
+----+------+--------------+

Call
+----+------+--------------+
| id | date | phone_number |
+----+------+--------------+
| 1  | 0945 | 111111111111 |
+----+------+--------------+
| 2  | 0950 | 222222222222 |
+----+------+--------------+
| 3  | 1045 | 333333333333 |
+----+------+--------------+

如何找出哪些电话是由人,他们提出phone_number是不是在Phone_book?所需的输出将是:

Call
+----+------+--------------+
| id | date | phone_number |
+----+------+--------------+
| 3  | 1045 | 333333333333 |
+----+------+--------------+

任何帮助将非常感激。

Answers:


438

有几种不同的方法可以执行此操作,效率各不相同,具体取决于查询优化器的性能以及两个表的相对大小:

这是最简短的说明,如果您的电话簿很短,则可能最快:

SELECT  *
FROM    Call
WHERE   phone_number NOT IN (SELECT phone_number FROM Phone_book)

或者(由于Alterlife

SELECT *
FROM   Call
WHERE  NOT EXISTS
  (SELECT *
   FROM   Phone_book
   WHERE  Phone_book.phone_number = Call.phone_number)

或(感谢WOPR)

SELECT * 
FROM   Call
LEFT OUTER JOIN Phone_Book
  ON (Call.phone_number = Phone_book.phone_number)
  WHERE Phone_book.phone_number IS NULL

(如其他人所说,忽略它通常最好只选择想要的列,而不是' *')


1
避免输入,使用
EXISTS-

28
在一般情况下,左外部联接可能最快,因为它可以防止子查询的重复执行。
WOPR

不要挑剔,但是我建议的子查询返回<code>
select'x

是的-MySQL手册建议这对于“ EXISTS”查询是正常的
Alnitak

2
@Alnitak:在第二个查询中,您不需要SELECT *在子查询中。相反,例如,SELECT 1就足够了。
亚历山大·阿巴库莫夫

90
SELECT Call.ID, Call.date, Call.phone_number 
FROM Call 
LEFT OUTER JOIN Phone_Book 
  ON (Call.phone_number=Phone_book.phone_number) 
  WHERE Phone_book.phone_number IS NULL

应该删除子查询,以使查询优化器发挥作用。

另外,请避免使用“ SELECT *”,因为如果有人更改基础表或视图(效率低下),它可能会破坏您的代码。


10
通常,这是最有效的方法,因为它不会在第二张桌子上进行多次通过...希望某些人正在阅读喜剧。
Nerdfest

3
我希望人们能简要介绍一下:除非您是顶级SQL性能专家,否则很难事先知道最快的速度(这取决于您使用的DBMS引擎)。
bortzmeyer

2
在这种情况下,大O符号会很容易地告诉您可以期望的最快速度。它的数量级不同。
Jonesopolis

如果您的两个表之间存在关系,请参阅来世的回答和我的评论1:N。或添加DISTINCT如被看见在弗拉多的答案
ToolmakerSteve

25

当处理较大的数据集时,下面的代码将比上面给出的答案更有效率。

SELECT * FROM Call WHERE 
NOT EXISTS (SELECT 'x' FROM Phone_book where 
Phone_book.phone_number = Call.phone_number)

1
与往常一样,值得针对目标数据集分析查询的性能,以选择性能最佳的查询。如今,SQL优化器已经足够好了,其性能结果通常令人惊讶。
格雷格(Greg Hewgill)

1
这种方法的优势(与WOPR的LEFT OUTER JOIN相比)是Call,如果中有多个匹配的行,则可以避免每行返回多行Phone_book。也就是说,如果1:N您的两个表之间存在关系。
ToolmakerSteve

我将从这一点开始-它直接代表了意图。如果性能不够好,请确保存在适当的索引。只有这样,才能尝试不太明显的LEFT OUTER JOIN,看看它的性能是否更好。
ToolmakerSteve

6
SELECT DISTINCT Call.id 
FROM Call 
LEFT OUTER JOIN Phone_book USING (id) 
WHERE Phone_book.id IS NULL

这将返回Phone_book表中缺少的额外ID。


4

我认为

SELECT CALL.* FROM CALL LEFT JOIN Phone_book ON 
CALL.id = Phone_book.id WHERE Phone_book.name IS NULL

表中的id列与call表中的id列的值不同Phone_book,因此您不能使用这些值进行连接。有关类似方法,请参见WOPR的答案。
Michael Fredrickson '02

3
SELECT t1.ColumnID,
CASE 
    WHEN NOT EXISTS( SELECT t2.FieldText  
                     FROM Table t2 
                     WHERE t2.ColumnID = t1.ColumnID) 
    THEN t1.FieldText
    ELSE t2.FieldText
END FieldText       
FROM Table1 t1, Table2 t2

如果同一列的另一表中不存在数据,则这将从您的表中返回数据
Harvinder Sidhu 2013年


1

或者,

select id from call
minus
select id from phone_number

1
不确定是否按原样(尽管是MINUS)回答了这个问题,但这个运算符是否是新增加的。这最终导致了质量低下的队列-您可能希望增强此答案。
ste-fu
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.