太多JOIN的替代解决方案

时间:2014-02-25 07:16:52

标签: sql sql-server sql-server-2008 tsql

有一个包含所有名称的表格:

CREATE TABLE Names(
  Name VARCHAR(20)
)

并且有多个具有类似架构的表。 让我们说:

CREATE TABLE T1
(
  Name VARCHAR(20),
  Description VARCHAR(30),
  Version INT
)

CREATE TABLE T2
(
  Name VARCHAR(20),
  Description VARCHAR(30),
  Version INT
)

我需要按照优先级查询每个名称的描述:

  1. T1中匹配名称且版本= 1
  2. 的所有记录
  3. T1中匹配名称和版本= 2
  4. 的所有记录
  5. T2中具有匹配名称且版本= 1
  6. 的所有记录
  7. T2中具有匹配名称和版本= 2
  8. 的任何记录

    只有在没有来自更高优先级源的结果时才需要来自较低优先级源的结果。

    到目前为止,我已经有了:

    SELECT 
    N.Name AS Name, Description = 
    CASE
    WHEN (T11.Description IS NOT NULL) THEN T11.Description
    WHEN (T12.Description IS NOT NULL) THEN T12.Description
    WHEN (T21.Description IS NOT NULL) THEN T21.Description
    WHEN (T22.Description IS NOT NULL) THEN T22.Description
    ELSE NULL
    END
    FROM Names AS N
    LEFT JOIN T1 AS T11 ON T11.Name = N.Name AND T11.Version = 1 
    LEFT JOIN T1 AS T12 ON T12.Name = N.Name AND T12.Version = 2
    LEFT JOIN T2 AS T21 ON T21.Name = N.Name AND T21.Version = 1 
    LEFT JOIN T2 AS T22 ON T22.Name = N.Name AND T22.Version = 2
    

    这是有效的,但这里有JOIN太多了吗?有没有更好的方法?

    sqlfiddle

    示例输入:

    INSERT INTO Names VALUES('name1')
    INSERT INTO Names VALUES('name2')
    INSERT INTO Names VALUES('name3')
    INSERT INTO Names VALUES('name4')
    INSERT INTO Names VALUES('name5')
    INSERT INTO Names VALUES('name6')
    
    INSERT INTO T1 VALUES ('name1','name1_T1_1', 1)
    INSERT INTO T1 VALUES ('name2','name2_T1_1', 1)
    INSERT INTO T1 VALUES ('name3','name3_T1_1', 1)
    INSERT INTO T1 VALUES ('name3','name3_T1_2', 2)
    INSERT INTO T1 VALUES ('name5','name5_T1_2', 2)
    
    INSERT INTO T2 VALUES ('name1','name1_T2_1', 1)
    INSERT INTO T2 VALUES ('name4','name4_T2_1', 1)
    

    例外结果:

    --
    --  Excepted result:
    --  Name      Description
    --  name1      name1_T1_1
    --  name2      name2_T1_1
    --  name3      name3_T1_1
    --  name4      name4_T2_1
    --  name5      name5_T1_2
    --  name6      NULL
    

3 个答案:

答案 0 :(得分:1)

嗯,这是一个消除case语句并最小化查询重复部分的解决方案,当然它需要一些自己的连接,所以你需要相当多的表和/或版本来从中获得任何真正的好处:

;WITH
AllDescriptions AS
(
    SELECT 1 AS Rank, * FROM T1
    UNION ALL SELECT 2 AS Rank, * FROM T2
    -- UNION ALL SELECT 3 AS Rank, * FROM T3
    -- UNION ALL SELECT 4 AS Rank, * FROM T4
    -- etc
),
Ranks AS
(
    SELECT
        AllDescriptions.Name,
        MIN(AllDescriptions.Rank) AS Rank
    FROM
        AllDescriptions
    GROUP BY
        Name
),
Versions AS
(
    SELECT
        AllDescriptions.Name,
        AllDescriptions.Rank,
        MIN(AllDescriptions.Version) AS Version
    FROM
        AllDescriptions
        INNER JOIN Ranks
            ON Ranks.Name = AllDescriptions.Name
            AND Ranks.Rank = AllDescriptions.Rank
        GROUP BY
            AllDescriptions.Name,
            AllDescriptions.Rank
),
Descriptions AS
(
    SELECT
        AllDescriptions.Name,
        AllDescriptions.Description
    FROM
        AllDescriptions
        INNER JOIN Versions
            ON Versions.Name = AllDescriptions.Name
            AND Versions.Rank = AllDescriptions.Rank
            AND Versions.Version = AllDescriptions.Version
)
SELECT
    Names.*,
    Descriptions.Description
FROM
    Names
    LEFT OUTER JOIN Descriptions
        ON Descriptions.Name = Names.Name

答案 1 :(得分:1)

select n.name, isnull(d.description,d1.Description) description
from Names n
outer apply (select top 1 t1.Name, t1.Description
             from T1
             WHERE t1.Name = n.name
             order by Version asc   
            ) d
outer apply (select top 1 t2.Name, t2.Description
            from T2
             WHERE t2.Name = n.name
             order by Version asc   
            ) d1

答案 2 :(得分:0)

尝试此查询,它也会为您提供预期的结果。

SELECT N.name AS Name, 
       Description = 
           CASE 
               WHEN ( t1.description IS NOT NULL ) THEN t1.description 
               WHEN ( t2.description IS NOT NULL ) THEN t2.description 
               ELSE NULL 
           END 
FROM   names AS N 
       LEFT JOIN t1 
              ON t1.name = N.name 
                 AND t1.version IN( 1, 2 ) 
       LEFT JOIN t2 
              ON t2.name = N.name 
                 AND t2.version IN ( 1, 2 )