如何在SQL Server中将多行员工组合成单行

时间:2015-02-26 07:32:00

标签: sql sql-server pivot

我有下表,下面是预期的结果。如果有一种简单的方法可以在SQL服务器中获得预期的结果,请告诉我。

EmpNo Name   Benefit   StartDate     Status
--------------------------------------------
0001  ABC    Medical   01/01/2014    Active
0001  ABC    Dental    02/02/2013    Inactive
0001  ABC    Vision    03/03/2012    Active
0002  XYZ    Medical   01/01/2014    Active
0002  XYZ    Dental    02/02/2008    Inactive

结果应如下所示

EmpNo   Name  MedicalStart MedStatus  DenStart  DenStatus VisionStart     VisStatus
---------------------------------------------------------------------------------------
0001    ABC   01/01/2014    Active    02/02/2013    Inactive   03/03/2012    Active
0002    XYZ   01/01/2014    Active    02/02/2008    Inactive                       .

我在最初的帖子中忘记了几个笔记。

1)有10个福利计划可供使用,因此员工可以注册任意数量的计划,最多10个计划(所有计划或少数计划或根本没有计划)。

2)每个EmpNo / Name只有一行具有相同的福利计划。

3)此外,每行都有几个字段,例如,选举选项(Self,Family等)等等。为简单起见,我没有提出这个问题。

4 个答案:

答案 0 :(得分:3)

示例数据:

CREATE TABLE #Test
(
    EmpNo INT
    , Name VARCHAR(255)
    , Benefit VARCHAR(255)
    , StartDate DATETIME2
    , Status VARCHAR(255)
);
INSERT INTO #Test
    (EmpNo, Name, Benefit, StartDate, Status)
VALUES
    (0001, 'ABC', 'Medical', '01/01/2014', 'Active')
    , (0001, 'ABC', 'Dental', '02/02/2013', 'Inactive')
    , (0001, 'ABC', 'Vision', '03/03/2012', 'Active')
    , (0002, 'XYZ', 'Medical', '01/01/2014', 'Active')
    , (0002, 'XYZ', 'Dental', '02/02/2008', 'Inactive')

一个简单的小组条款:

实际查询(如果有历史记录),使用ROW_NUMBER可以让您找到每个用户及其收益的最新记录:

SELECT T.EmpNo
    , T.Name
    , MAX(CASE WHEN T.Benefit = 'Medical ' THEN CONVERT(VARCHAR(10), CONVERT(DATE, T.StartDate, 106), 103) END) AS MedStart
    , MAX(CASE WHEN T.Benefit = 'Medical' THEN T.Status END) AS MedStatus
    , MAX(CASE WHEN T.Benefit = 'Dental ' THEN CONVERT(VARCHAR(10), CONVERT(DATE, T.StartDate, 106), 103) END) AS DenStart
    , MAX(CASE WHEN T.Benefit = 'Dental' THEN T.Status END) AS DenStatus
    , MAX(CASE WHEN T.Benefit = 'Vision ' THEN CONVERT(VARCHAR(10), CONVERT(DATE, T.StartDate, 106), 103) END) AS VisStart
    , MAX(CASE WHEN T.Benefit = 'Vision' THEN T.Status END) AS VisStatus
FROM (
    SELECT ROW_NUMBER() OVER (PARTITION BY EmpNo, Name, Benefit ORDER BY StartDate DESC) AS RowNo
        , EmpNo
        , Benefit
        , Name
        , StartDate
        , Status
    FROM #Test
) AS T
WHERE T.RowNo = 1
GROUP BY T.EmpNo
    , T.Name

如果存在未知数量的优惠,则使用动态SQL进行查询。可能效率不高:

DECLARE @SQL NVARCHAR(MAX) = 'SELECT T.EmpNo, T.Name'
    , @Benefit VARCHAR(MAX);

SELECT @SQL += ', MAX(CASE WHEN T.Benefit = ''' + Benefit + ''' THEN CONVERT(VARCHAR(10), CONVERT(DATE, T.StartDate, 106), 103) END) AS ' + LEFT(Benefit, 3) + 'Star
                , MAX(CASE WHEN T.Benefit = ''' + Benefit + ''' THEN T.Status END) AS ' + LEFT(Benefit, 3) + 'Status'
FROM (SELECT DISTINCT Benefit FROM #Test) AS T

SET @SQL += '
FROM (
    SELECT ROW_NUMBER() OVER (PARTITION BY EmpNo, Name, Benefit ORDER BY StartDate DESC) AS RowNo, EmpNo, Benefit, NAME, StartDate, STATUS
    FROM #Test
    ) AS T
WHERE T.RowNo = 1
GROUP BY T.EmpNo, T.Name'

EXEC sp_executesql @SQL

查询(如果没有历史记录):

SELECT T.EmpNo
    , T.Name
    , MAX(CASE WHEN T.Benefit = 'Medical ' THEN CONVERT(VARCHAR(10), CONVERT(DATE, T.StartDate, 106), 103) END) AS MedStart
    , MAX(CASE WHEN T.Benefit = 'Medical' THEN T.Status END) AS MedStatus
    , MAX(CASE WHEN T.Benefit = 'Dental ' THEN CONVERT(VARCHAR(10), CONVERT(DATE, T.StartDate, 106), 103) END) AS DenStart
    , MAX(CASE WHEN T.Benefit = 'Dental' THEN T.Status END) AS DenStatus
    , MAX(CASE WHEN T.Benefit = 'Vision ' THEN CONVERT(VARCHAR(10), CONVERT(DATE, T.StartDate, 106), 103) END) AS VisStart
    , MAX(CASE WHEN T.Benefit = 'Vision' THEN T.Status END) AS VisStatus
FROM #Test AS T
GROUP BY T.EmpNo
    , T.Name

<强>输出:

EmpNo   Name    MedStart    MedStatus   DenStart    DenStatus   VisStart    VisStatus
-------------------------------------------------------------------------------------
1       ABC     01/01/2014  Active      02/02/2013  Inactive    03/03/2012  Active
2       XYZ     01/01/2014  Active      02/02/2008  Inactive    NULL        NULL

答案 1 :(得分:1)

PIVOT字段上的

StartDate解决方案:

DECLARE @tb AS TABLE
(
    EmpNo INT
    ,Name NVARCHAR(25)
    ,Benefit NVARCHAR(25)
    ,StartDate DATE
    ,[Status] NVARCHAR(25)
);

INSERT INTO @tb VALUES (1, 'ABC', 'Medical', '01/01/2014', 'Active');
INSERT INTO @tb VALUES (1, 'ABC', 'Dental', '02/02/2013', 'Inactive');
INSERT INTO @tb VALUES (1, 'ABC', 'Vision', '03/03/2012', 'Active');
INSERT INTO @tb VALUES (2, 'XYZ', 'Medical', '01/01/2014', 'Active');
INSERT INTO @tb VALUES (2, 'XYZ', 'Dental', '02/02/2012', 'Inactive');

SELECT EmpNo
    ,Name
    ,MAX(MedicalStart) AS MedicalStart
    ,MAX(MedStatus) AS MedStatus
    ,MAX(DenStart) AS DenStart
    ,MAX(DenStatus) AS DenStatus
    ,MAX(VisionStart) AS VisionStart
    ,MAX(VisStatus) AS VisStatus
FROM
(
    SELECT EmpNo
        ,Name
        ,[Medical] AS MedicalStart
        ,CASE 
            WHEN [Medical] IS NOT NULL AND [Status] = 'Active' THEN 'Active' 
            WHEN [Medical] IS NOT NULL AND [Status] = 'Inactive' THEN 'Inactive' 
            ELSE NULL END AS MedStatus
        ,[Dental] AS DenStart
        ,CASE 
            WHEN [Dental] IS NOT NULL AND [Status] = 'Active' THEN 'Active' 
            WHEN [Dental] IS NOT NULL AND [Status] = 'Inactive' THEN 'Inactive' 
            ELSE NULL END AS DenStatus
        ,[Vision] AS VisionStart
        ,CASE 
            WHEN [Vision] IS NOT NULL AND [Status] = 'Active' THEN 'Active' 
            WHEN [Vision] IS NOT NULL AND [Status] = 'Inactive' THEN 'Inactive' 
            ELSE NULL END AS VisStatus
        ,[Status]
    FROM @tb
    PIVOT
    (
        MAX(StartDate)
        FOR Benefit IN ([Medical], [Dental], [Vision])
    ) AS pivotTableDate
) AS tb
GROUP BY EmpNo, Name;

您可以查看this link 'PIVOT on two or more fields in SQL Server'以获取有关完整PIVOT解决方案的信息。

答案 2 :(得分:0)

我假设对于每个员工,您最多有1行用于医疗,最多1行用于牙科,最多1行用于视力。如果是这样,你可以这样做:

select 

    t.EmpNo, t.Name, 
    tMedical.MedicalStart, tMedical.MedicalStatus

from 
(
    select
        EmpNo, Name
    from 
        TableName
    group by EmpNo, Name
) t

left outer join

(

    select 
        EmpNo, Name, Benefit, 
        min(StartDate) as MedicalStart, 
        min(Status) as MedicalStatus
    from 
        TableName
    where
        Benefit = 'Medical'
    group by EmpNo, Name, Benefit

) tMedical on t.EmpNo = tMedical.EmpNo and t.Name = tMedical.Name

left outer join ... 

类似于tMedical,您可以在此处将左连接添加到tDentaltVision。这应该是它。

答案 3 :(得分:-1)

您可以使用Outer Apply执行此操作。我假设一名员工可以拥有任意数量的医疗,牙科和视力行。此查询将针对每种类型采用最新的StartDate

Select EmpNo, Name, Medical.StartDate MedicalStart, Medical.Status MedStatus, Dental.StartDate DenStart, Dental.Status DenStatus, Vision.StartDate VisionStart, Vision.Status VisStatus
From (Select Distinct EmpNo, Name From TableName) Emp
Outer Apply (Select Top 1 StartDate, Status From TableName Med Where Benefit='Medical' and Med.EmpNo=Emp.EmpNo Order By StartDate Desc) Medical
Outer Apply (Select Top 1 StartDate, Status From TableName Den Where Benefit='Dental' and Den.EmpNo=Emp.EmpNo Order By StartDate Desc) Dental
Outer Apply (Select Top 1 StartDate, Status From TableName Vis Where Benefit='Vision' and Vis.EmpNo=Emp.EmpNo Order By StartDate Desc) Vision

请告诉我这是否适合您......