如何选择另一个表中没有匹配条目的行?


323

我正在对数据库应用程序进行一些维护工作,但我发现,即使一个表中的值以外键的形式使用,但表上没有外键约束。

我正在尝试在这些列上添加FK约束,但是我发现,由于先前的错误已被天真纠正,因此表中已经存在大量不良数据,因此我需要查找那些行匹配另一个表,然后将其删除。

我在网络上找到了一些此类查询的示例,但它们似乎都提供了示例而不是说明,而且我不理解它们为什么起作用。

有人可以向我解释如何构造一个查询,该查询返回另一个表中没有匹配项的所有行,以及它在做什么,以便我可以自己进行这些查询,而不是为混乱中的每个表运行SO 没有FK限制?

Answers:


612

这是一个简单的查询:

SELECT t1.ID
FROM Table1 t1
    LEFT JOIN Table2 t2 ON t1.ID = t2.ID
WHERE t2.ID IS NULL

关键点是:

  1. LEFT JOIN用来; 它将返回来自的所有行Table1,而不管其中是否有匹配的行Table2

  2. WHERE t2.ID IS NULL条款;这会将返回的结果限制为仅返回ID Table2为null的那些行,换句话说,没有Table2针对该ID的特定记录Table1Table2.ID对于Table1ID中与ID不匹配的所有记录,将以NULL的形式返回Table2


4
如果失败的ID为NULL
迈克尔

168
@Michael-如果NULL您的架构中的ID有效,那么您可能会遇到更大的问题,您是否同意?:)
rinogo

1
即使table1的记录多于table2的记录,这也可以吗?如果table1有100条记录,而table2有200条记录(100个匹配/联接和100个不匹配/联接),我们将得到全部200条记录吗?
Juan Velez

1
我经常喜欢将左联接包装为子查询/内联视图,以确保WHERE子句和LEFT JOIN之间没有相互作用。
安德鲁·沃尔夫

1
@Jas答案的关键点1,来自第一个表的所有行,即使是那些不符合t1.ID = t2.ID左连接条件的行。如果将第一行更改为SELECT t1.ID, t2.ID并删除WHERE行,您将更好地了解其工作原理。
彼得·拉伯什(PeterLaboš),

97

我将使用EXISTS表达式,因为它更强大,例如,如果LEFT JOIN您必须处理联接表中的所有内容,则可以更精确地选择要联接的行。它的效率可能与LEFT JOIN使用null测试的情况相同。

SELECT t1.ID
FROM Table1 t1
WHERE NOT EXISTS (SELECT t2.ID FROM Table2 t2 WHERE t1.ID = t2.ID)

查询优化器可以轻松地处理这种简单操作,以实现最佳执行效果。
安德鲁·沃尔夫

2
是的,它的主要优点EXISTS是可变性。
Ondrej Bozek '17

1
简单,优雅,它解决了我的问题!好一个!
MikeMighty

2
实际上将我的一次查询速度从7秒降低到了200毫秒...(与相比WHERE t2.id IS NULL)谢谢。
Moti Korets

4
@MotiKorets,您的意思是提高速度:)
Ondrej Bozek,

14
SELECT id FROM table1 WHERE foreign_key_id_column NOT IN (SELECT id FROM table2)

表1有一列要添加外键约束,但foreign_key_id_columnid表2 中的并不完全匹配。

  1. 初始选择列出了idtable1中的。这些将是我们要删除的行。
  2. NOT INwhere语句中的子句将查询限制为仅查询foreign_key_id_columnid2s 列表中未包含的行。
  3. SELECT括号中的语句将获得id表2 中所有的列表。

@ zb226:的链接与IN带字面值列表的子句限制有关。它不适用于IN对子查询的结果使用子句。该问题的公认答案实际上是通过使用子查询解决了该问题。(大量的文字值列表是有问题的,因为它创建了一个巨大的SQL表达式。子查询可以正常工作,因为即使结果列表很大,SQL表达式本身也很小。)
Kannan Goundan

@KannanGoundan你是绝对正确的。撤回有缺陷的评论。
zb226 '19

8

T2您要在其中添加约束的表在哪里:

SELECT *
FROM T2
WHERE constrained_field NOT
IN (
    SELECT DISTINCT t.constrained_field
    FROM T2 
    INNER JOIN T1 t
    USING ( constrained_field )
)

并删除结果。


4

让我们有以下两个表(薪水表和员工表) 在此处输入图片说明

现在我要从雇员表中那些不在薪水中的记录。 我们可以通过3种方式做到这一点:

  1. 使用内部联接
select * from employee
where id not in(select e.id from employee e inner join salary s on e.id=s.id)

在此处输入图片说明

  1. 使用左外部联接
select * from employee e 
left outer join salary s on e.id=s.id  where s.id is null

在此处输入图片说明

  1. 使用完全联接
select * from employee e
full outer join salary s on e.id=s.id where e.id not in(select id from salary)

在此处输入图片说明



0

您可以选择如下所示的视图

CREATE VIEW AuthorizedUserProjectView AS select t1.username as username, t1.email as useremail, p.id as projectid, 
(select m.role from userproject m where m.projectid = p.id and m.userid = t1.id) as role 
FROM authorizeduser as t1, project as p

然后在视图上进行选择或更新:

select * from AuthorizedUserProjectView where projectid = 49

产生的结果如下图所示,即对于不匹配的列,已填充null。

[Result of select on the view][1]

0

我不知道哪个被优化了(与@AdaTheDev相比),但是我使用时似乎更快(对我来说至少)

SELECT id  FROM  table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2

如果要获取任何其他特定属性,可以使用:

SELECT COUNT(*) FROM table_1 where id in (SELECT id  FROM  table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2);


-2

你可以做这样的事情

   SELECT IFNULL(`price`.`fPrice`,100) as fPrice,product.ProductId,ProductName 
          FROM `products` left join `price` ON 
          price.ProductId=product.ProductId AND (GeoFancingId=1 OR GeoFancingId 
          IS NULL) WHERE Status="Active" AND Delete="No"

-6

如何在双向表中选择没有匹配条目的行?

    从[dbo]中选择*。[EmppDetails] e
     在[e.Gid = d.Gid]上右加入[Employee]。[Gender] d
    其中e.Gid为Null

    联盟 
    从[dbo]中选择*。[EmppDetails] e
     在e.Gid = d.Gid上左加入[Employee]。[Gender] d
    d.Gid为空

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.