TSQL枢转多个列

时间:2018-07-18 17:40:10

标签: sql sql-server tsql sql-server-2014

代码:

DECLARE @Employee TABLE
    (
        [Employee_Id] INT IDENTITY(1, 1)
      , [Code]        NVARCHAR(10)
    ) ;

INSERT INTO @Employee
VALUES ( N'E1' ), ( N'E2' ), ( N'E3' ) ;

DECLARE @Contact TABLE
    (
        [Employee_Id]  INT
      , [PhoneType]    CHAR(1)
      , [PhoneNumber]  VARCHAR(20)
      , [IsMainNumber] BIT
    ) ;

INSERT INTO @Contact
VALUES (1, 'M', '1234567890', 1), (1, 'H', '1234567891', 0),
       (1, 'M', '1234567892', 0), (1, 'B', '1234567893', 0),
       (2, 'M', '2234567890', 0), (2, 'H', '2234567891', 1),
       (2, 'B', '2234567892', 0), (2, 'M', '2234567893', 0),
       (3, 'M', '3234567890', 0), (3, 'H', '3234567891', 0),
       (3, 'M', '3234567892', 0), (3, 'B', '3234567893', 1);

SELECT  
    [E].[Employee_Id],
    [E].[Code],
    [COA].[MainPhoneNumber],
    [COA].[NonMainNumber]
FROM
    @Employee AS [E]
OUTER APPLY
    (SELECT      
         MAX (IIF([C].[IsMainNumber] = 1, [C].[PhoneNumber], NULL)) [MainPhoneNumber],
         MAX (IIF([C].[IsMainNumber] = 0, [C].[PhoneNumber], NULL)) [NonMainNumber]
     FROM        
         @Contact AS [C]
     WHERE       
         [E].[Employee_Id] = [C].[Employee_Id]
     GROUP BY    
         [C].[Employee_Id]) AS [COA] ;

当前输出

Employee_Id Code    MainPhoneNumber NonMainNumber
1           E1      1234567890      1234567893
2           E2      2234567891      2234567893
3           E3      3234567893      3234567892

目标

我需要返回MAX主要电话号码及其电话类型和MAX非主要电话号码及其电话类型。我可以获取MAX主要/非主要电话号码,但需要以某种方式获取其电话类型。我不想基于Employee_Id和PhoneNumber进行两个额外的联接并获取类型,因为原始表很大并且会减慢很多速度。试图找出性能良好的替代方案。

所需的输出

Employee_Id Code    MainPhoneType   MainPhoneNumber NonMainPhoneType    NonMainNumber
1           E1      M               1234567890      B                   1234567893
2           E2      H               2234567891      M                   2234567893
3           E3      B               3234567893      M                   3234567892

3 个答案:

答案 0 :(得分:1)

似乎您需要两个apply

select e.Employee_Id, e.Code, 
       c.PhoneType as MainPhoneType, c.PhoneNumber as MainPhoneNumber, 
       c1.PhoneType as NonMainPhoneType, c1.PhoneNumber as NonMainNumber
from @Employee e outer apply
     (select top (1) c.PhoneType, c.PhoneNumber
      from @Contact c
      where c.Employee_Id = e.Employee_Id and
            c.IsMainNumber = 1
      order by c.phonetype 
     ) c outer apply 
     (select top (1) c1.PhoneType, c1.PhoneNumber
      from @Contact c1
      where c1.Employee_Id = e.Employee_Id and
            c1.IsMainNumber = 0
      order by c1.phonetype 
     ) c1;

如果您不想两次执行JOIN,则可以使用临时表,只需转储具有相关index的联系人

  

#temp(Employee_Id,IsMainNumber)包括(PhoneType,PhoneNumber)

insert into #temp (Employee_Id, PhoneType, PhoneNumber, IsMainNumber)
    select Employee_Id, PhoneType, PhoneNumber, IsMainNumber
    from (select *, row_number() over (partition by Employee_Id, IsMainNumber order by PhoneType) as seq
          from @Contact
         ) c
     where seq = 1    

现在,您无需再次使用@Contact

select e.*, m.*
from @Employee e cross apply
     (select max(case when t.IsMainNumber = 1 then t.PhoneType end) as MainPhoneType, 
             max(case when t.IsMainNumber = 1 then t.PhoneNumber end) as MainPhoneNumber,
             max(case when t.IsMainNumber = 0 then t.PhoneType end) as NonMainPhoneType, 
             max(case when t.IsMainNumber = 0 then t.PhoneNumber end) as NonMainNumber
      from #temp t
      where t.Employee_Id = e.Employee_Id               
     ) m;

答案 1 :(得分:1)

不太确定如何确定所需的nonMainNumber。似乎大多数示例数据都有几行可以返回。我将把这项工作留给您。这是您可以为此使用一些条件聚合的方法。

select x.Employee_Id
    , x.Code
    , MainPhoneType = max(case when x.RowNum = 1 then x.PhoneType end)
    , MainPhoneNumber = max(case when x.RowNum = 1 then x.PhoneNumber end)
    , NonMainPhoneType = max(case when x.RowNum = 2 then x.PhoneType end)
    , NonMainPhoneNumber = max(case when x.RowNum = 2 then x.PhoneNumber end)
from
(
    select e.Employee_Id
        , e.Code
        , c.PhoneType
        , c.PhoneNumber
        , RowNum = ROW_NUMBER() over(partition by e.Employee_Id order by c.IsMainNumber desc, c.PhoneType) --Not sure how you determine the non MainNumber when there are several to pick from
    from @Employee e
    join @Contact c on c.Employee_Id = e.Employee_Id
) x
group by x.Employee_Id
    , x.Code

答案 2 :(得分:1)

您可以通过条件聚合来做到这一点:

select e.Employee_Id, e.Code
       max(case when seqnum = 1 and c.PhoneType = 'M' then c.PhoneType end) as MainPhoneType
       max(case when seqnum = 1  and c.PhoneType = 'M' then x.PhoneNumber end) as MainPhoneNumber,
       max(case when seqnum = 1 and c.PhoneType <> 'M' then c.PhoneType end) as NonMainPhoneType
       max(case when seqnum = 1  and c.PhoneType <> 'M' then c.PhoneNumber end) as NonMainPhoneNumber
from @Employee e join
     (select c.*,
             row_number() over (partition by c.Employee_Id
                                             (case when PhoneType = 'M' then  'M' end)
                                order by c.PhoneNumber desc
                               ) as seqnum
      from @Contact c
     ) c
     on c.Employee_Id = e.Employee_Id
group by e.Employee_Id, e.Code;

此逻辑中的关键思想是partition by子句。它将两种类型的电话分为两组-'M'代表“主要”,NULL代表其他所有。