如何在 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
,您可以在此处向 tDental
和 tVision
添加左连接。应该就是这样。
示例数据:
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 解决方案的信息。
我正在关注 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
,您可以在此处向 tDental
和 tVision
添加左连接。应该就是这样。
示例数据:
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 解决方案的信息。