如何在 SQL 服务器中将多行员工合并为单行

How to combine multiple rows of employee into single row in SQL Server

我正在关注 table,以下是预期结果。如果有一种简单的方法可以在 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                       .

我忘记在我的首字母中添加一些注释 post。

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

2) 每个 EmpNo/Name.

只有一行具有相同的福利计划

3) 此外,每一行都有多个字段,例如,选举选项(自我、家庭等)等等。为了简单起见,我没有包括在问题中。

我假设对于每个员工,医疗最多 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 添加左连接。应该就是这样。

示例数据:

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

您可以使用 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

让我知道这是否适合你...

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 解决方案的信息。