自我左连接重复

时间:2016-01-11 18:54:28

标签: sql-server duplicates left-join distinct self-join

我试图将存储在垂直模型中的数据表转移到更加水平的SQL Server表格模型中。不幸的是由于数据的性质,我不能在这里使用真实数据,所以我编写了一个遵循相同模型的通用示例。

表格有三列,ID,列ID和值,其中ID和列ID构成主键。此外,不需要任何数据(即ID可能缺少列ID = 3而不会破坏任何内容)

PetID | ColumnID | Value
---------------------------
1     | 1        | Gilda
1     | 2        | Cat
2     | 1        | Sonny
2     | 2        | Cat
2     | 3        | Black

由于主键是两列的组合,我不能使用内置的PIVOT功能,所以我尝试自我左键连接:

SELECT T1.PetID
    ,T2.Value AS [Name]
    ,T3.Value AS [Type]
    ,T4.Value AS [Color]
FROM @Temp AS T1
LEFT JOIN @Temp AS T2 ON T1.PetID = T2.PetID
    AND T2.ColumnID = 1
LEFT JOIN @Temp AS T3 ON T1.PetID = T3.PetID
    AND T3.ColumnID = 2
LEFT JOIN @Temp AS T4 ON T1.PetID = T4.PetID
    AND T4.ColumnID = 3;

我的想法是,我想从T1获取ID,然后执行自我LEFT JOIN以通过ColumnID获取每个值。但是,我在数据中得到重复:

PetID | Name  | Type | Color
------------------------------
1     | Gilda | Cat  | NULL
1     | Gilda | Cat  | NULL
2     | Sonny | Cat  | Black
2     | Sonny | Cat  | Black
2     | Sonny | Cat  | Black

我能够使用DISTINCT删除这些重复项,但数据集相当大,因此所需的排序操作极大地减慢了查询速度。有没有更好的方法来实现这一点,或者我只是坚持一个缓慢的查询?

4 个答案:

答案 0 :(得分:1)

您可以使用CASE语句并完全避免加入。

SELECT
    PetID,
    MAX(CASE WHEN ColumnID = 1 THEN Value ELSE NULL END) AS Name,
    MAX(CASE WHEN ColumnID = 2 THEN Value ELSE NULL END) AS Type,
    MAX(CASE WHEN ColumnID = 3 THEN Value ELSE NULL END) AS Color
FROM @Temp
GROUP BY PetId

PetID, ColumnID必须成为您正常工作的主要关键。否则,当同一ColumnID

多次使用相同PetID时,会导致问题

答案 1 :(得分:1)

如果你愿意,你可以使用支点。

SELECT  *
FROM    (SELECT PetID,
                (CASE ColumnID
                   WHEN 1 THEN 'Name'
                   WHEN 2 THEN 'Type'
                   WHEN 3 THEN 'Color'
                 END) ValueType,
                VALUE
         FROM   @Temp
        ) t 
PIVOT
(   MAX(Value) 
    FOR ValueType IN ([Name],[Type],[Color]) 
) p

没有Sub查询的另一种方法是..

SELECT  PetID,
        [1] [Name],
        [2] [Type],
        [3] [Color]
FROM    @Temp
PIVOT
(   MAX(Value) 
    FOR ColumnID IN ([1],[2],[3]) 
) p

答案 2 :(得分:0)

我不了解您对排序的担忧。你有一个主键,所以你也有一个索引。这是正确的方法:

FROM (select distinct PetID from @Temp) AS T1

复制的修复很简单,但也可能会提高性能:

{{1}}

答案 3 :(得分:0)

SELECT T1.PetID
    ,T1.Value AS [Name]
    ,T2.Value AS [Type]
    ,T3.Value AS [Color]
    --select *
FROM #Temp AS T1
LEFT JOIN #Temp AS T2 ON T1.PetID = T2.PetID
    AND T2.ColumnID = 2
LEFT JOIN #Temp AS T3 ON T1.PetID = T3.PetID
    AND T3.ColumnID = 3
where t1.ColumnID = 1

您的问题是您正在加入具有多行的主表。