【问题标题】:How to return rows from left table not found in right table?如何从右表中找不到左表返回行?
【发布时间】:2014-10-30 09:18:07
【问题描述】:

我有两个列名相似的表,我需要从左表返回右表中没有的记录?我有一个主键(列),可以帮助我比较两个表。首选哪个联接?

【问题讨论】:

标签: sql join left-join outer-join


【解决方案1】:

试试这个

SELECT f.*
FROM first_table f LEFT JOIN second_table s ON f.key=s.key
WHERE s.key is NULL

更多信息请阅读这篇文章:Joins in Sql Server

【讨论】:

  • 键是 IS NULL 不是 = NULL
  • @HaydenThring s.key = NULL 表示在第二个表中找不到匹配的行,所以我认为条件为真
  • 对不起,我的意思是“让它工作的关键是”不要与在这个答案中使用“key”作为变量混淆
  • 在左连接中,A&B 将按照图表输出。如果在语句末尾添加“WHERE s.key is NULL”会删除 B 中的结果,您能否澄清一下?
【解决方案2】:

如果您要求使用 T-SQL,那么让我们先看看基础知识。这里有三种类型的连接,每种都有自己的一组逻辑处理阶段:

  1. cross join 是最简单的。它只实现一个逻辑查询处理阶段,Cartesian Product。此阶段对作为连接输入提供的两个表进行操作,并生成两者的笛卡尔积。也就是说,一个输入的每一行都与另一个输入的所有行匹配。因此,如果您在一个表中有 m 行,而在另一个表中有 n 行,则结果中会出现 m×n 行。
  2. 然后是 Inner joins :它们应用两个逻辑查询处理阶段:A Cartesian product 在两个输入表之间作为交叉连接,然后是 filters 基于您在 ON 子句中指定的谓词的行(也称为Join condition)。
  3. 接下来是第三种连接,Outer Joins

    outer join 中,通过在表名之间使用关键字LEFT OUTER JOINRIGHT OUTER JOINFULL OUTER JOIN 将表标记为preserved 表。 OUTER 关键字是 optionalLEFT 关键字表示保留left table 的行; RIGHT 关键字表示保留right table 中的行;而FULL 关键字意味着保留bothleftright 表中的行。

    outer join 的第三个逻辑查询处理阶段根据 ON 谓词识别保留表中未在其他表中找到匹配项的行。此阶段将这些行添加到连接的前两个阶段生成的结果表中,并使用NULL 标记作为这些外部行中连接的非保留侧的属性的占位符。

现在如果我们看问题:要从左表返回在右表中找不到的记录,请使用Left outer join 并从连接右侧过滤出具有NULL 值的行.

【讨论】:

  • 很有教育意义的答案
【解决方案3】:

我也喜欢使用 NOT EXISTS。就性能而言,如果索引正确,它的性能应该与 LEFT JOIN 相同或更好。此外,它更易于阅读。

SELECT Column1
FROM TableA a
WHERE NOT EXISTS ( SELECT Column1
                   FROM Tableb b
                   WHERE a.Column1 = b.Column1
                 )

【讨论】:

    【解决方案4】:

    除了代码示例之外,我无法在其他两个答案中添加任何内容:但是,我发现看到它的实际效果会很有用(在我看来,其他答案更好,因为它们解释了它)。

    DECLARE @testLeft TABLE (ID INT, SomeValue VARCHAR(1))
    DECLARE @testRight TABLE (ID INT, SomeOtherValue VARCHAR(1))
    
    INSERT INTO @testLeft (ID, SomeValue) VALUES (1, 'A')
    INSERT INTO @testLeft (ID, SomeValue) VALUES (2, 'B')
    INSERT INTO @testLeft (ID, SomeValue) VALUES (3, 'C')
    
    
    INSERT INTO @testRight (ID, SomeOtherValue) VALUES (1, 'X')
    INSERT INTO @testRight (ID, SomeOtherValue) VALUES (3, 'Z')
    
    SELECT l.*
    FROM 
        @testLeft l
         LEFT JOIN 
        @testRight r ON 
            l.ID = r.ID
    WHERE r.ID IS NULL 
    

    【讨论】:

    • 如果您使用的是 Mysql,请将最后一行 WHERE r.ID IS NULL 替换为 WHERE ISNULL(r.ID)
    【解决方案5】:

    从左表中选择*,其中关键字段不在(从右表中选择关键字段)

    【讨论】:

    • 您能解释一下您的代码为何有效吗?还可以使用格式化工具更好地格式化您的答案。
    • 这是解决问题的正确方法之一。但是,如果我们使用 10k、20k 条记录,它的性能并不好
    【解决方案6】:

    此页面提供了不同连接类型的详细分类,以及维恩图可视化,以帮助...嗯...可视化连接的差异。

    正如 cmets 所说,从它的声音来看,这是一个非常基本的查询,因此您应该尝试了解连接之间的差异以及它们的实际含义。

    查看http://blog.codinghorror.com/a-visual-explanation-of-sql-joins/

    您正在寻找如下查询:

    DECLARE @table1 TABLE (test int)
    DECLARE @table2 TABLE (test int)
    
    INSERT INTO @table1
    (
        test
    )
    SELECT 1
    UNION ALL SELECT 2
    
    INSERT INTO @table2
    (
        test
    )
    SELECT 1
    UNION ALL SELECT 3
    
    -- Here's the important part
    SELECT  a.*
    FROM    @table1 a
    LEFT    join @table2 b on a.test = b.test -- this will return all rows from a
    WHERE   b.test IS null -- this then excludes that which exist in both a and b
    
    -- Returned results:
    
    2
    

    【讨论】:

      【解决方案7】:

      这是一个现实生活中的例子,我被要求提供过去 6 个月内从我们网站购买但不是过去 3 个月内购买的用户列表。

      对我来说,我能想到的最容易理解的方式是这样的:

      --Users that bought from us 6 months ago and between 3 months ago.
      DECLARE @6To3MonthsUsers table (UserID int,OrderDate datetime)
      INSERT @6To3MonthsUsers
          select u.ID,opd.OrderDate
              from OrdersPaid opd
              inner join Orders o
              on opd.OrderID = o.ID
              inner join Users u
              on o.BuyerID = u.ID
              where 1=1 
              and opd.OrderDate BETWEEN DATEADD(m,-6,GETDATE()) and DATEADD(m,-3,GETDATE())
      
      --Users that bought from us in the last 3 months
      DECLARE @Last3MonthsUsers table (UserID int,OrderDate datetime)
      INSERT @Last3MonthsUsers
          select u.ID,opd.OrderDate
              from OrdersPaid opd
              inner join Orders o
              on opd.OrderID = o.ID
              inner join Users u
              on o.BuyerID = u.ID
              where 1=1 
              and opd.OrderDate BETWEEN DATEADD(m,-3,GETDATE()) and GETDATE()
      

      现在,有了这两张表,我只需要从 @6To3MonthsUsers 表中获取不在 @Last3MonthsUsers 表中的用户。

      有两种简单的方法可以实现:

      1. 使用左连接:

        select distinct a.UserID
        from @6To3MonthsUsers a
        left join @Last3MonthsUsers b
        on a.UserID = b.UserID
        where b.UserID is null
        
      2. 不在:

        select distinct a.UserID
        from @6To3MonthsUsers a
        where a.UserID not in (select b.UserID from @Last3MonthsUsers b)
        

      两种方式都会得到相同的结果,我个人更喜欢第二种方式,因为它更具可读性。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-05-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-17
        • 1970-01-01
        • 2019-05-09
        相关资源
        最近更新 更多