在INNER JOIN和LEFT JOIN之间切换

时间:2019-11-25 13:03:18

标签: sql sql-server join

我有以下查询:

DECLARE @Cond_ition int = 2

SELECT T.Id, T.Description, T.A_Id, T.B_Id, T.C_Id FROM Table1 as T
JOIN TableA as A ON A.Id = T.A_Id AND A.GroupId = @Cond_ition
JOIN TableB as B ON B.Id = T.B_Id AND B.GroupId = @Cond_ition
JOIN TableC as C ON C.Id = T.C_Id AND C.GroupId = @Cond_ition

列A_Id,B_Id和C_Id在T的各行中具有少量的不同值。 变量@Cond_ition实际上是该查询需要在其中运行的存储过程的参数。

查询将仅从T中返回在A,B和C中具有匹配项的行,除非这些表中的一个或多个表不具有匹配项,否则这很好。 如果例如B为空或与A不匹配,则查询应返回T中与A和C匹配的行。如果B和C为空,则我需要T中与A匹配的行。全部为空或没有匹配项,那么我需要来自T的所有行。 因此,如果有匹配项,则应充当INNER JOIN,但如果没有匹配项,则应充当LEFT JOIN。

关于如何从联接中获得这种双重“效果”的任何想法?

*编辑:根据要求,我添加了示例数据

Table T  
Id Description A_Id   B_Id    C_Id  
-- ----------- ----   ----    ----  
1  desc1       FANTA  CAN     LIGHT  
2  something2  SPRITE BOTTLE  LIGHT  
3  more3       SPRITE CAN     ZERO  
4  name4       7UP    BOTTLE  ZERO  
5  label5             GLASS   REGULAR  

**Case 1**
Table A
Id     GroupId
--     -------  
FANTA  2
SPRITE 2

Table B
Id   GroupId
--   -------  
CAN  5

*Expected result*
rows 1, 2 and 3

**Case 2**
Table A
Id     GroupId
--     -------  
FANTA  2
SPRITE 2

Table B
Id   GroupId
--   -------  
CAN  2
CAN  5

*Expected result*
rows 1 and 3

**Case 3**
Id     GroupId
--     -------  
FANTA  2
SPRITE 2

Table B
Id   GroupId
--   -------  
CAN  2
CAN  5

Table C
Id   GroupId
--   -------  
ZERO 2

*Expected result*
row 3

**Case 4**
Tables A, B and C empty or no rows with GroupId = 2

*Expected result*
all rows of T

2 个答案:

答案 0 :(得分:4)

如果我的理解正确,那么您只希望包含匹配项的行(如果有)。如果根本没有匹配的行,那么您需要table1中的所有行。

您可以使用UNION ALL处理此问题:

WITH tabc as (
      SELECT T.Id, T.Description, T.A_Id, T.B_Id, T.C_Id
      FROM Table1 T LEFT JOIN
           TableA A
           ON A.Id = T.A_Id AND A.GroupId = @Cond_ition LEFT JOIN
           TableB B
           ON B.Id = T.B_Id AND B.GroupId = @Cond_ition LEFT JOIN
           TableC C
           ON C.Id = T.C_Id AND C.GroupId = @Cond_ition
      WHERE T.A_Id IS NOT NULL OR T.B_Id IS NOT NULL OR T.C_Id IS NOT NULL
     )
SELECT tabc.*
FROM tabc
UNION ALL
SELECT t.*, NULL, NULL, NULL
FROM table1 t
WHERE NOT EXISTS (SELECT 1 FROM tabc);

答案 1 :(得分:0)

感谢@gordonlinoff的投入,它帮助我找到了解决方案。 这是符合我要求的查询:

SELECT T.Id, T.Description, T.A_Id, T.B_Id, T.C_Id
FROM Table1 T
JOIN (SELECT Id FROM TableA WHERE GroupId = @Cond_ition
      UNION
      SELECT TOP(1) '' WHERE NOT EXISTS (SELECT TOP(1) Id FROM TableA WHERE GroupId = @Cond_ition)
     )
  AS A ON ISNULL(T.A_Id,'') = CASE
    WHEN NOT EXISTS (SELECT TOP(1) Id FROM TableA WHERE GroupId = @Cond_ition) THEN ISNULL(T.A_Id,'')
    ELSE A.Id
    END
JOIN (SELECT Id FROM TableB WHERE GroupId = @Cond_ition
      UNION
      SELECT TOP(1) '' WHERE NOT EXISTS (SELECT TOP(1) Id FROM TableB WHERE GroupId = @Cond_ition)
     )
  AS B ON ISNULL(T.B_Id,'') = CASE
    WHEN NOT EXISTS (SELECT TOP(1) Id FROM TableB WHERE GroupId = @Cond_ition) THEN ISNULL(T.B_Id,'')
    ELSE B.Id
    END
JOIN (SELECT Id FROM TableC WHERE GroupId = @Cond_ition
      UNION
      SELECT TOP(1) '' WHERE NOT EXISTS (SELECT TOP(1) Id FROM TableC WHERE GroupId = @Cond_ition)
     )
  AS C ON ISNULL(T.C_Id,'') = CASE
    WHEN NOT EXISTS (SELECT TOP(1) Id FROM TableC WHERE GroupId = @Cond_ition) THEN ISNULL(T.C_Id,'')
    ELSE C.Id
    END

我在表A,表B和表C的联接中使用UNION。
首先选择获取与条件匹配的行,然后在没有匹配记录的情况下与常量合并。使用常量可确保UNION的结果永远不会为空。

在JOIN的ON子句中,我用CASE再次检查是否没有匹配的记录,在这种情况下,我将加入T本身的字段(使用ISNULL将返回所有行),否则我将加入子表。

我用想要查询的人更新了dbfiddle的查询。