【问题标题】:How do I keep all values FROM Table 1 while joining multiple tables with conditions to Table 2, which has a LEFT JOIN to Table 1?如何在将具有条件的多个表连接到表 2 时保留表 1 中的所有值,表 2 具有到表 1 的 LEFT JOIN?
【发布时间】:2022-12-09 17:01:58
【问题描述】:

要求:从第一个表中列出所有客户,然后从第二个表和第三个表中提取任何符合条件的类,第三个表仅连接到第二个。

我试过的:

  1. 当我在第三个表上使用 JOIN 时,我没有得到所有的客户(例如,“Bobby Black”)。
  2. 当我在第三张表上使用 LEFT JOIN 时,我得到了所有的客户,但是 然后是所有课程(例如,Jane Doe 的“PST”课程)!

    简化示例:

    DECLARE @T1_Customers TABLE 
    (T1_Customer_id INT, 
     T1_FName VARCHAR(50), 
     T1_LName VARCHAR(50))
    INSERT INTO @T1_Customers VALUES 
    (1,'John','Darwin'),
    (2,'Jane','Doe'),
    (3,'Bobby','Black')
    
    DECLARE @T2_Classes TABLE 
    (T2_Class_id INT, 
     T2_Customer_id INT, 
     T2_ClassType_id INT, 
     T2_ClassName VARCHAR(50), 
     T2_Status VARCHAR(50))
    INSERT INTO @T2_Classes VALUES 
    (1,1,1,'Emergency Medical Dispatch v1','Pass'),
    (2,1,2,'Emergency Medical Dispatch Instructor','Pass'),
    (3,2,3,'Public Safety Telecommunicator','Pass'),
    (4,2,1,'Emergency Medical Dispatch v1','Pass'),
    (5,2,1,'Emergency Medical Dispatch v2','Fail')
    
    DECLARE @T3_ClassTypes TABLE 
    (T3_ClassType_id INT, 
     T3_ClassType VARCHAR(50))
    INSERT INTO @T3_ClassTypes VALUES 
    (1,'EMD'),
    (2,'EMD-I'),
    (3,'PST')
    
    --SELECT * FROM @T1_Customers SELECT * FROM @T2_Classes SELECT * FROM @T3_ClassTypes
    
    --FIRST ATTEMPT
    SELECT * FROM @T1_Customers
      LEFT JOIN @T2_Classes 
        ON T2_Customer_id = T1_Customer_id 
          AND T2_Status != 'Fail'
      JOIN @T3_ClassTypes 
        ON T3_ClassType_id = T2_ClassType_id
          AND T3_ClassType != 'PST'
    
    --SECOND ATTEMPT    
    SELECT * FROM @T1_Customers
      LEFT JOIN @T2_Classes 
        ON T2_Customer_id = T1_Customer_id 
          AND T2_Status != 'Fail'
      LEFT JOIN @T3_ClassTypes 
        ON T3_ClassType_id = T2_ClassType_id
          AND T3_ClassType != 'PST'
    

    尝试结果和预期结果:(T2_ClassName 缩写)

    第一次尝试

     T1_Customer_id T1_FName  T1_LName  T2_Class_id  T2_Customer_id  T2_ClassType_id  T2_ClassName  T2_Status  T3_ClassType_id  T3_ClassType 
     -------------- --------- --------- ------------ --------------- ---------------- ------------- ---------- ---------------- ------------ 
     1              John      Darwin    1            1               1                EMD v1        Pass       1                EMD          
     1              John      Darwin    2            1               2                EMDI          Pass       2                EMD-I        
     2              Jane      Doe       4            2               1                EMD v1        Pass       1                EMD          
    

    第二次尝试

     T1_Customer_id T1_FName  T1_LName  T2_Class_id  T2_Customer_id  T2_ClassType_id  T2_ClassName  T2_Status  T3_ClassType_id  T3_ClassType 
     -------------- --------- --------- ------------ --------------- ---------------- ------------- ---------- ---------------- ------------ 
     1              John      Darwin    1            1               1                EMD v1...     Pass       1                EMD          
     1              John      Darwin    2            1               2                EMDI...       Pass       2                EMD-I        
     2              Jane      Doe       3            2               3                PST...        Pass       null             null          
     2              Jane      Doe       4            2               1                EMD v1...     Pass       1                EMD          
     3              Bobby     Black     null         null            null             null          null       null             null         
    

    期望的结果

     T1_Customer_id T1_FName  T1_LName  T2_Class_id  T2_Customer_id  T2_ClassType_id  T2_ClassName  T2_Status  T3_ClassType_id  T3_ClassType 
     -------------- --------- --------- ------------ --------------- ---------------- ------------- ---------- ---------------- ------------ 
     1              John      Darwin    1            1               1                EMD v1        Pass       1                EMD          
     1              John      Darwin    2            1               2                EMDI          Pass       2                EMD-I        
     2              Jane      Doe       4            2               1                EMD v1        Pass       1                EMD          
     3              Bobby     Black     null         null            null             null          null       null             null         
    

【问题讨论】:

  • 请添加几行示例数据和预期结果。 T2 上的左连接不会确保 T1 中的所有行都将出现在结果集中。
  • mysql 不是 sql-server;请只标记您的实际数据库
  • @ysth - 对不起。有人建议添加标签“mysql”,我认为这是 sql-server 的代码类型。我会删除它。
  • @TheImpaler - 我在我的 DECLARES 中提供了我尝试的示例数据,并解释了我在引用该数据时的要求......?
  • 您的预期输出是什么?

标签: sql sql-server join left-join sql-server-2016


【解决方案1】:

加入 INNER 加入 @T2_Classes@T3_ClassTypes ,然后执行 LEFT 加入 @T1_Customers 到该结果集:

SELECT * 
FROM @T1_Customers t1
LEFT JOIN (
  SELECT *
  FROM @T2_Classes t2 INNER JOIN @T3_ClassTypes t3
  ON t3.T3_ClassType_id = t2.T2_ClassType_id
  WHERE t2.T2_Status <> 'Fail' AND t3.T3_ClassType <> 'PST'
) t 
ON t.T2_Customer_id = t1.T1_Customer_id;

请参阅demo

【讨论】:

  • 谢谢...好奇,如果每个表都是唯一命名的,是否有任何理由需要所有别名(“t1”、“t2”、“t3”)?我在没有别名的情况下尝试了这个,在最后一个“ON”之前只保留“t”,并且它有效。我只在多次使用同一个表名时才使用别名。
  • @DanielT 你确定不会有列名冲突吗?我看到在您的示例数据中,您通过应用表示表的前缀 T?_ 来命名列。虽然这似乎是一个很好的命名约定,但它并不是一种常见的做法。大多数开发人员会在没有该前缀的情况下命名列。因此,不带别名的 ON 子句:ON ClassType_id = ClassType_id 会抛出类似“列名‘ClassType_id’不明确”的错误。这就是为什么使用别名始终是一种好的做法。
  • 谢谢...这个数据库的每个表都有一个唯一的列前缀,只有一个例外,这与该项目无关...也就是说,我认为唯一需要的别名是末尾 ON 之前的“t”。即使从“t.T2Customer”中删除了该别名,此代码也能正常工作……我想这个问题的答案只是“最佳实践”,而不是解决方案的任何特定内容,对吧?
  • @DanielT 你可以在这些线程中得​​到一些想法:stackoverflow.com/questions/198196/when-to-use-sql-table-aliasstackoverflow.com/questions/3718737/…
  • 因此,听起来如果您的数据库前缀正确(即表但主要是列),则仅在某些情况下才需要别名。但是,如果有人看到这篇文章并且有一个数据库不像我的那样设置(带有前缀)我会将其标记为已接受的答案,即使提供的许多其他类似解决方案确实有效(我标记为有用),包括OUTER APPLY,这可能会更好,但我只是没有使用 APPLY,我不确定它是否对我的情况有那么大的影响......谢谢!
【解决方案2】:

这看起来是一个很好的案例外部应用:

select c.*, cl.*
from customers c
outer apply(
  select cl.Class_Id, cl.ClassName, cl.[Status], ct.ClassType
  from Classes cl
    join ClassTypes ct on ct.ClassType_Id = cl.ClassType_Id
  where cl.Customer_Id = c.Customer_Id 
    and cl.[Status] != 'Fail'
    and ct.ClassType != 'PST'
)cl;

Demo Fiddle

【讨论】:

  • 谢谢...根据您的 DemoFiddle,它看起来可行,但我选择了一个我可以更好地管理和更熟悉的解决方案,因为我从未使用过 OUTER APPLY。
  • @DanielT - apply() 是一个经常被忽视的运算符,自从SQL Server 2005,我会建议熟悉它 - 它被称为横向连接,可以在许多情况下提供显着的性能优势。
【解决方案3】:

添加

where T3_ClassType is not null or T2_Customer_id is null

到你的第二次尝试

【讨论】:

    【解决方案4】:

    您通过引入一个在 T2 上过滤的位置将其转换为隐式内部联接。

    SELECT * FROM @T1_Customers
      LEFT JOIN (select * from @T2_Classes
      inner JOIN @T3_ClassTypes 
        ON T3_ClassType_id = T2_ClassType_id
      where T2_Status != 'Fail' AND T3_ClassType != 'PST') tmp
        ON T2_Customer_id = T1_Customer_id ;
    

    DBFiddle demo

    【讨论】:

      【解决方案5】:

      这是一条不适合 cmets 部分的评论。两个左连接的输出有什么问题?

       T1_Cust T1_FName  T1_LName  T2_Class_id  T2_Customer_id  T2_ClassType_id  T2_ClassName  T2_Status  T3_ClassType_id  T3_ClassType 
       ------- --------- --------- ------------ --------------- ---------------- ------------- ---------- ---------------- ------------ 
       1       John      Darwin    1            1               1                E V1          Pass       1                EMD          
       1       John      Darwin    2            1               2                EMDI          Pass       2                EMD-I        
       2       Jane      Doe       3            2               3                PST           Pass       null             null         
       2       Jane      Doe       4            2               1                EMDv1         Pass       1                EMD          
       3       Bobby     Black     null         null            null             null          null       null             null         
      

      【讨论】:

      • 不应列出 PST 类...如前所述,我的示例得到了简化,我有更多的表可以进一步分解条件,这些表没有连接到第一个表,只有第二个表或之后的表。
      猜你喜欢
      • 2022-12-01
      • 1970-01-01
      • 2020-08-24
      • 2020-11-10
      • 2021-11-24
      • 2017-12-20
      • 2016-10-05
      • 1970-01-01
      • 2021-03-20
      相关资源
      最近更新 更多