【问题标题】:Full outer join misbehaving全外连接行为不端
【发布时间】:2013-10-10 21:22:55
【问题描述】:

我在同一个 sql-server 数据库中有两个表,用于存储连续两个季度的应用程序名称和用户名(以及其他内容)。我想要的是一个完全联接,它显示表 A 中不在表 B 中的用户,反之亦然。

编辑: table_a 和 table_b 都包含两个或多个记录,其中 AppName 和 Username 具有相同的值,因此需要 DISTINCT。我编辑了下面的示例以反映这一点。

table_a:

-----------------------
| AppName  | Username |
-----------------------
| app1     | jdoe     |
| app1     | jsmith   |
| app1     | jdoe     |
| app2     | jsmith   |
-----------------------

table_b:

-----------------------
| AppName  | Username |
-----------------------
| app1     | fbar     |
| app1     | jsmith   |
| app1     | jboehner |
| app1     | fbar     |
| app3     | jboehner |
-----------------------

SQL:

SELECT DISTINCT a.username as q2, b.username as q3
FROM tablea as a
FULL JOIN tableb as b
ON a.username = b.username
WHERE a.appname = 'app1'
AND b.appname = 'app1'

预期结果:

-----------------------
| q2       | q3       |
-----------------------
| jdoe     |          |
| jsmith   | jsmith   |
|          | fbar     |
-----------------------
|          | jboehner |
-----------------------

实际结果:

-----------------------
| q2       | q3       |
-----------------------
| jsmith   | jsmith   |
-----------------------

什么给了??

【问题讨论】:

    标签: sql sql-server


    【解决方案1】:

    在 OUTER JOINed 表(在这种情况下是您的两个表)上使用 WHERE 子句将其更改为 INNER JOIN,因为过滤器应用于连接结果并且 所有 条件必须是遇见了。在您的情况下,除了匹配的行(它强制它进行 INNER JOIN)外,这是不可能的。我怀疑这就是你的意思:

    SELECT DISTINCT a.username as q2, b.username as q3
    FROM dbo.tablea as a
    FULL OUTER JOIN dbo.tableb as b
    ON a.username = b.username
    AND (a.appname = b.appname)
    WHERE a.appname = 'app1' OR b.appname = 'app1';
    
    -- or
    
    SELECT DISTINCT a.username as q2, b.username as q3
    FROM dbo.tablea as a
    FULL OUTER JOIN dbo.tableb as b
    ON a.username = b.username
    AND (a.appname = b.appname)
    WHERE 'app1' IN (a.appname, b.appname);
    

    【讨论】:

    • 你不能在 a.username = b.username 和 a.appname = b.appname 上执行 FULL OUTER JOIN,然后将 WHERE 条件放在事实之后,因为你已经创建了数据集NULL 在适当的地方?
    • SQL Server 是否能够以这种方式将过滤器推送到各个表中?我习惯于手动将过滤器下推到派生表中。
    • @Love2Learn 是的,但重要的是 WHERE 子句不能对两个表都有 AND(这使其成为 INNER JOIN)。
    • 在全连接的情况下,任何一个表上的 where 子句都不会导致内连接或左外连接吗?只需将 where 条件添加到 on 子句即可。
    【解决方案2】:
    SELECT  DISTINCT a.username as q2, b.username as q3
    FROM    (SELECT x.* FROM tablea x WHERE x.appname = 'app1') as a
    FULL JOIN (SELECT y.* FROM tableb y WHERE y.appname = 'app1') as b 
    ON a.username = b.username
    

    编辑#1:

    USE AdventureWorks2008R2;
    SET STATISTICS IO ON;
    SET NOCOUNT ON;
    
    PRINT 'Test #1 '
    PRINT '{'
    SELECT DISTINCT x.Name, y.Name
    FROM
    (
        SELECT  p.*
        FROM    Production.Product p
        WHERE   p.Name = 'HL Road Frame'
    ) x 
    FULL OUTER JOIN
    (
        SELECT  pm.*
        FROM    Production.ProductModel pm
        WHERE   pm.Name = 'HL Road Frame'
    ) y ON x.ProductModelID = y.ProductModelID
    PRINT '}'
    GO 
    
    PRINT 'Test #2 '
    PRINT '{'
    SELECT DISTINCT p.Name, pm.Name
    FROM Production.Product p
    FULL OUTER JOIN Production.ProductModel pm
    ON p.ProductModelID = pm.ProductModelID
    AND (p.Name = pm.Name)
    WHERE p.Name = 'HL Road Frame' OR pm.Name = 'HL Road Frame'
    PRINT '}'
    GO 
    
    SET STATISTICS IO OFF;
    

    编辑#2:

    Test #1 
    {
    Table 'ProductModel'. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'Product'. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    }
    Test #2 
    {
    Table 'ProductModel'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'Product'. Scan count 1, logical reads 15, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    }
    

    编辑#3:

    【讨论】:

    • @AaronBertrand 如果表在 appname 上建立索引,这很可能会产生一个非常有效的计划。使用正确的索引,这甚至可能导致没有排序的合并连接。事实上,如果过滤器向外移动,我看不出查询如何变得更快。
    • 我很感兴趣。考虑使用 pastebin。
    • @AaronBertrand (应用程序名称,用户名)上的索引包括 (*) 怎么样?如果 appname 上的过滤器具有高度选择性并且表很大,则包含应该是可选的。
    【解决方案3】:

    where 应该是或不是,并且: 其中 a.appname = 'app1' 或 b.appname = 'app1'

    【讨论】:

    • 不,这将允许任一表引入 appname <> 'app1' 所在的行。
    猜你喜欢
    • 2019-10-17
    • 2015-10-11
    • 2012-07-27
    • 2022-01-23
    • 2018-04-16
    • 1970-01-01
    • 2012-04-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多