【问题标题】:Left outer join SQL Server左外连接 SQL Server
【发布时间】:2012-12-09 02:10:26
【问题描述】:

我很困惑为什么这不起作用,我已经多次使用这种类型的语法,但这个语法让我大吃一惊。

我有两张桌子;

tableA

regId name    regStatus
1     George  1
2     Jenny   1
3     Penny   1
4     James   1
5     Preston 1
6     Jamie   0

表B

activeRegId passiveRegID Status
1            2           1
1            3           1
1            4           0
6            1           1

我要做的是从tableA 返回所有行,不包括(tableA.regstatus = 0)(tableB.status = 1 for a user regid = 1) 的行。

我想避免使用NOT IN (select ...)

到目前为止我的查询:

    select top 10 
        tA.regId, tA.name
    from 
        tableA tA 
    left OUTER JOIN 
        tableB tB ON tB.activeRegId = tA.regid AND tB.passiveRegID <> 1 
                     AND tB.status <> 1 AND tB.passiveRegID IS NULL
    where 
        tA.regStatus = 1
        and tA.regid <> 1

我所期待的应该如下,但是,除了 Jamie 之外,我将获得 tableA 中的所有用户。

regId name
4     James
5     Preston

【问题讨论】:

    标签: sql sql-server-2005 outer-join


    【解决方案1】:

    我认为您需要将一些谓词移出 LEFT OUTER JOIN 的 ON 子句,而是将它们作为谓词放在 WHERE 子句中。

    根据您提供的数据,我无法确定谓词到底应该是什么,但是您在 LEFT OUTER JOIN 的 ON 子句中包含的任何谓词仅用于从表 B 中排除行,不是 表 A。

    使用 LEFT OUTER JOIN,您仍将获得表 A 的所有记录,除非它们被 WHERE 子句中的谓词排除。

    编辑 基于 cmets,我认为这会起作用,其中“loggedInUserId”是登录用户表 A 中的 regId:

    SELECT top 10
       tA.regId,
       tA.name
    FROM tableA tA1
    JOIN tableA tA2
    ON (tA1.regId <> tA2.regId
        AND tA2.regStatus <> 0)
    LEFT OUTER JOIN tableB tB
    ON (tA1.regId = tB.activeRegId
        AND tA2.regId = tB.passiveRegID
        AND tB.Status <> 0)
    WHERE tA1.regId = 'loggedInUserId'
    AND tB.activeRegId IS NULL
    

    同时排除那些已经阻止登录用户的人:

    SELECT top 10
       tA.regId,
       tA.name
    FROM tableA tA1
    JOIN tableA tA2
    ON (tA1.regId <> tA2.regId
        AND tA2.regStatus <> 0)
    LEFT OUTER JOIN tableB tB
    ON (
        --finding blocked users
        (tA1.regId = tB.activeRegId
         AND tA2.regId = tB.passiveRegID
         AND tB.Status <> 0)
        OR
        --finding blocking users
        (tA2.regId = tB.activeRegId
         AND tA1.regId = tB.passiveRegId
         AND tB.Status <> 0)
        )
    WHERE tA1.regId = 'loggedInUserId'
    AND tB.activeRegId IS NULL
    

    【讨论】:

    • 您建议的答案返回的数据与我从原始查询中获得的数据相同。您提到了更多详细信息/数据,我拥有的表格就是这样(tableA dob、lastname 等还有几列)tableA=userDetails tableB=blocks。因此,tableA 中的用户将能够看到 tableA 中的所有人,除了他们自己,但是,他们可以阻止任何他们想要的人。所以 tableB 持有 regid=1 (aka activeRegID) 决定不查看 (regid=2,3,4 aka passiveRegId), regStatus=0 (deleted user) tableB.status=0 (user was blocked but has been unblocked) 的 id ,我需要保留历史记录)。
    • 啊,所以您想要一个列出谁可以看到谁的查询?或者只是 George 可以看到的人的列表(因为 George 是唯一一个设置了“障碍”的人)?
    • 正确,假设 George 已登录,他只会看到 James 和 Preston。如果 Preston 已登录,他将看到除 Jamie 之外的所有人,因为 Jamie 已被删除。
    • 下面的查询可以做到,但我不想使用“IN”,因为我有一个大型数据库。此外,在这个答案的后面,我将通过添加任何阻止 George 的人也不应该在列表中来扩展查询。 SELECT top 10 tA.regId FROM tableA tA where tA.regId NOT IN (SELECT passiveRegID from tableB WHERE activeRegId = 1 AND status=1) AND tA.regStatus = 1 AND tA.regId 1
    • 成功了,感谢您抽出宝贵时间。如果您有兴趣阅读您、Glenn 和我的“NOT IN”解决方案之间的执行计划,我已经在 Glenn 的帖子中添加了一些 cmets。再次感谢。
    【解决方案2】:

    如果你不想使用NOT IN,那么使用EXCEPT来排除你不想要的呢?

    SELECT regId
      FROM tableA
    EXCEPT
    SELECT tA.regId
      FROM tableA tA
      JOIN tableB tB ON (tB.activeRegId = tA.regid AND tB.passiveRegID = 1 AND tB.status = 1)
      WHERE tA.regStatus = 0
    

    【讨论】:

    • 我从未使用过 EXCEPT,甚至不知道你有这样的事情。多么尴尬!你知道使用 EXCEPT 是否比 'NOT IN' 或使用左外连接有更好的性能吗?
    • 将视情况而定,但EXCEPTUNION 通常最终是一些最简单的集合操作。其他风格的 sql(如 Oracle)使用术语 MINUS 而不是 `EXCEPT'。
    • 在过去的 6 个月里,我一直在与 Oracle 合作,并注意到诸如 MINUS 之类的小宝石,但我从未见过有人在 MSSQL 中使用 EXCEPT。以防万一您想知道,执行计划几乎与 chamila_c 解决方案 2 嵌套循环相同,成本为 0%,并具有 2 次索引查找和 1 次索引扫描。有趣的是,“NOT IN”在 1 个嵌套循环和 2 个索引扫描中显示最少。无论如何,我想我会选择 chamila_c 解决方案,因为用我需要添加的其他东西来扩展它会更容易。谢谢你的帮助,因为你,我今天学到了一些新东西。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-02
    • 1970-01-01
    • 1970-01-01
    • 2021-09-02
    • 1970-01-01
    相关资源
    最近更新 更多