关于join:SQL – 如何从右表中找不到左表中的行?

SQL - How to Return rows from left table not found in right table?

我有两个列名相似的表,需要从左表返回右表中找不到的记录?我有一个主键(列),它将帮助我比较两个表。首选哪种连接?


试试这个

1
2
3
SELECT f.*
FROM first_table f LEFT JOIN second_table s ON f.key=s.key
WHERE s.key IS NULL

有关详细信息,请阅读本文:加入SQL Server

enter image description here


如果您需要T-SQL,那么让我们先看看基本原理。这里有三种类型的联接,每种联接都有自己的一组逻辑处理阶段,如下所示:

  • cross join是最简单的。它只实现一个逻辑查询处理阶段,即Cartesian Product。这个阶段在作为连接的输入提供的两个表上运行,并生成这两个表的笛卡尔积。也就是说,一个输入中的每一行都与另一个输入中的所有行匹配。因此,如果一个表中有M行,另一个表中有N行,那么结果中会有M×N行。
  • 然后是Inner joins:它们在两个输入表之间应用两个逻辑查询处理阶段:A Cartesian product作为交叉联接,然后它根据您在ON子句(也称为Join condition中指定的谓词来执行filters行。
  • 接下来是第三种连接,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值的行作为联接右侧的属性。


    我也喜欢用不存在。在性能方面,如果索引正确,它应该与左联接执行相同或更好。而且更容易阅读。

    1
    2
    3
    4
    5
    6
    SELECT Column1
    FROM TableA a
    WHERE NOT EXISTS ( SELECT Column1
                       FROM Tableb b
                       WHERE a.Column1 = b.Column1
                     )

    我只能在另外两个答案中添加一个代码示例:但是,我发现看到它的实际效果是很有用的(在我看来,其他答案更好,因为它们解释了它)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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


    这个页面给出了不同连接类型的详细分解,以及维恩图可视化以帮助…好。。。可视化连接中的差异。

    正如注释所说,这是一个非常基本的查询,从它的发音来看,所以您应该尝试理解连接和它们实际含义之间的区别。

    查看http://blog.codingsurror.com/a-visual-explation-of-sql-joins/

    您正在查找一个查询,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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

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


    这是现实工作中的一个例子,我被要求提供过去6个月内从我们网站上购买的用户列表,但在过去3个月内没有。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    --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
    2
    3
    4
    5
    SELECT DISTINCT a.UserID
    FROM @6To3MonthsUsers a
    LEFT JOIN @Last3MonthsUsers b
    ON a.UserID = b.UserID
    WHERE b.UserID IS NULL
  • 不在:

    1
    2
    3
    SELECT DISTINCT a.UserID
    FROM @6To3MonthsUsers a
    WHERE a.UserID NOT IN (SELECT b.UserID FROM @Last3MonthsUsers b)
  • 两种方法都会得到相同的结果,我个人更喜欢第二种方法,因为它更具可读性。