【问题标题】:How to select rows with no matching entry in another table?如何在另一个表中选择没有匹配条目的行?
【发布时间】:2011-05-03 19:55:08
【问题描述】:

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

我正在尝试在这些列上添加 FK 约束,但我发现,因为表中已经有大量错误数据来自以前的错误,这些错误已经被天真地纠正了,我需要找到那些不匹配另一个表,然后删除它们。

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

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

【问题讨论】:

    标签: sql foreign-keys


    【解决方案1】:

    这是一个简单的查询:

    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 子句;这会将返回的结果限制为仅返回从 Table2 返回的 ID 为 null 的行 - 换句话说,在 Table2 中有 NO 记录对于来自 Table1 的特定 ID。对于来自 Table1 且 ID 与 Table2 不匹配的所有记录,Table2.ID 将返回为 NULL。

    【讨论】:

    • 如果 ID 为 NULL 则失败
    • @Michael - 如果NULL ID 在您的架构中有效,您可能会遇到更大的问题,您不同意吗? :)
    • 即使 table1 比 table2 有更多记录,这也能工作吗?如果 table1 有 100 条记录,而 table2 有 200 条记录(100 条匹配/加入,100 条不匹配/加入)我们会返回所有 200 条记录吗?
    • 我经常喜欢将左联接包装为子查询/内联视图,以确保 WHERE 子句和 LEFT JOIN 之间没有相互作用。
    • @Jas 答案的关键点 1,来自第一个表的所有行,甚至那些不匹配左连接的 t1.ID = t2.ID 条件的行。如果您将第一行更改为 SELECT t1.ID, t2.ID 并删除 WHERE 行,您将更好地了解这是如何工作的。
    【解决方案2】:

    我会使用EXISTS 表达式,因为它更强大,你可以例如更准确地选择您想要加入的行。对于LEFT JOIN,您必须获取连接表中的所有内容。它的效率可能与 LEFT JOIN 的情况相同,具有空约束。

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

    【讨论】:

    • 查询优化器可以轻松处理这么简单的事情以实现最佳执行。
    • 是的,EXISTS 的主要优点是它的可变性。
    • 实际上将我的一个查询的速度从 7 秒降低到 200 毫秒...(与 WHERE t2.id IS NULL 相比)谢谢。
    • @MotiKorets 你的意思是提高速度:)
    • 此方法的另一个优点是,如果在 phpMyAdmin 中工作,它可能会为行提供唯一键,因此您可以在可视界面中获得编辑、复制、删除选项,而不是使用连接 where您可能无法获得这些选项。
    【解决方案3】:
    SELECT id FROM table1 WHERE foreign_key_id_column NOT IN (SELECT id FROM table2)
    

    表 1 有一个您要添加外键约束的列,但 foreign_key_id_column 中的值与表 2 中的 id 并不完全匹配。

    1. 初始选择列出了表 1 中的 ids。这些将是我们要删除的行。
    2. where 语句中的NOT IN 子句将查询限制为仅对foreign_key_id_column 中的值不在表2 ids 的列表中的行进行查询。
    3. 括号中的SELECT 语句将获得表2 中所有ids 的列表。

    【讨论】:

    • @zb226:您的链接与对带有文字值列表的 IN 子句的限制有关。它不适用于使用带有子查询结果的IN 子句。该问题的公认答案实际上通过使用子查询解决了该问题。 (大量的文字值列表是有问题的,因为它会创建一个巨大的 SQL 表达式。子查询可以正常工作,因为即使结果列表很大,SQL 表达式本身也很小。)
    • 不是一个好的选择。想象一下,如果您正在查询大量数据。假设一百万行被提取并包含在子查询中,编译器再次必须将每一行与子查询中返回的所有 id 进行匹配。对我来说是一个很大的不。
    【解决方案4】:

    让我们有以下 2 个表(工资和雇员)

    现在我想要那些不在工资表中的员工表中的记录。 我们可以通过 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)
    

    【讨论】:

      【解决方案5】:

      T2 是要添加约束的表:

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

      并删除结果。

      【讨论】:

        【解决方案6】:

        来自MySQL Inner Join Query To Get Records Not Present in Other Table这里的类似问题我得到了这个工作

        SELECT * FROM bigtable 
        LEFT JOIN smalltable ON bigtable.id = smalltable.id 
        WHERE smalltable.id IS NULL
        

        smalltable 是您缺少记录的位置,bigtable 是您拥有所有记录的位置。查询列出了smalltable 中不存在但bigtable 中存在的所有记录。您可以将id 替换为任何其他匹配条件。

        【讨论】:

          【解决方案7】:

          您可以选择Views,如下所示:

          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
          

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

          [Result of select on the view][1]
          

          【讨论】:

            【解决方案8】:

            我不知道哪个是优化的(与 @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);
            

            【讨论】:

              【解决方案9】:

              你可以这样做

                 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"
              

              【讨论】:

                【解决方案10】:

                SELECT * FROM First_table 减 从另一个中选择 *

                【讨论】:

                  【解决方案11】:

                  如何选择Both表中没有匹配条目的行?

                  select * from [dbo].[EmppDetails] e right join [Employee].[Gender] d on e.Gid=d.Gid 其中 e.Gid 为 Null 联盟 select * from [dbo].[EmppDetails] e left join [Employee].[Gender] d on e.Gid=d.Gid 其中 d.Gid 为 Null

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2023-01-16
                    • 1970-01-01
                    • 1970-01-01
                    • 2020-10-10
                    相关资源
                    最近更新 更多