我有一个数据库,提供employeeID,job,effectiveDate和dept。如果一名员工从事过多项工作,他们将获得额外的一行数据。我的目标是将每个员工对应的行压缩为一个。基本上我需要一个来自db的查询,如下所示:
EmpID Job EffDate Dept
001 QB 01-01-2001 OFF
001 LB 01-01-2010 DEF
001 K 01-01-2005 SPEC
002 HC 01-01-2007 STAFF
003 P 01-01-2001 SPEC
003 CB 01-01-2002 DEF
输出如下:
EmpID Job1 EffDate1 Dept1 Job2 EffDate2 Dept2 Job3 EffDate3 Dept3
001 QB 01-01-2001 OFF K 01-01-2005 SPEC LB 01-01-2010 DEF
002 HC 01-01-2007 STAFF
003 P 01-01-2001 SPEC CB 01-01-2002 DEF
到目前为止,我已经这样做了:
SELECT
EmpNo
, Job
, EffDate
, Dept
, ROW_NUMBER() OVER (PARTITION BY EmpNo ORDER BY EffDate) AS RowNum
INTO #temp1
FROM JobHist
ORDER BY EffDate DESC
SELECT
JobHist.EmpNo
, JobHist.Job AS Job1
, JobHist.EjhJobDesc AS JobDesc1
, JobHist.EffDate AS EffDate1
, JobHist.Dept AS Dept1
, temp2.Job AS Job2
, temp2.EffDate AS EffDate2
, temp2.Dept AS Dept2
FROM #temp1 AS JobHist LEFT JOIN #temp1 AS temp2 ON JobHist.EmpNo = temp2.EmpNo AND temp2.RowNum = 2
WHERE JobHist.RowNum = 1
这很好用。问题是我需要创建很多列,而且我不想写所有代码20次。所以我想迭代使用WHILE命令。这是我在第二个SELECT语句中尝试的内容:
DECLARE @Flag INT
DECLARE @FlagPlus INT
SET @Flag = 1
SET @FlagPlus = (@Flag + 1)
WHILE(@Flag < 20)
BEGIN
SELECT
temp@Flag.EmpNo
, temp@Flag.Job AS Job@Flag
, temp@Flag.EjhJobDesc AS JobDesc@Flag
, temp@Flag.EffDate AS EffDate@Flag
, temp@Flag.Dept AS Dept@Flag
FROM #temp1 AS temp@Flag
LEFT JOIN #temp@Flag AS temp@FlagPlus
ON temp@Flag.EmpNo = temp@FlagPlus.EmpNo AND temp@FlagPlus.RowNum = @FlagPlus
WHERE JobHist.RowNum = 1
SET @Flag = (@Flag + 1)
SET @FlagPlus = (@FlagPlus + 1)
END
我知道这可能不会起作用,因为SQL无法理解我试图调用每个表和字段的命名约定。有没有办法使用强制转换或连接命令,我可以自动化该过程,所以它只是递增我要求的数字?
答案 0 :(得分:2)
首先,让我清楚地知道这不是问题的直接答案。但是,由于代码块很大,它也不适合评论,我觉得它确实增加了问题的价值。所以这就是......
拥有动态数量的列很少是一个很好的解决方案。如果使用XML是一个选项,我会选择不同的解决方案:
SELECT
e.EmpNo,
(SELECT
h.Job,
h.EffDate,
h.Dept
FROM JobHist h
WHERE e.EmpNo = h.EmpNo
ORDER BY EffDate DESC
FOR XML PATH('job'), ROOT('jobs'), TYPE
) Jobs
FROM (SELECT DISTINCT EmpNo FROM JobHist) e
答案 1 :(得分:1)
以下是解决方案。无论Emp的工作变更多少,它都会转移所有这些工作 如果你想只旋转20,那么设置@MAXCol = 20
编辑:忘记最后一行@SQL周围的括号
SELECT
EmpNo
, Job
, EffDate
, Dept
, ROW_NUMBER() OVER (PARTITION BY EmpNo ORDER BY EffDate) AS RowNum
INTO #temp1
FROM JobHist
ORDER BY EffDate DESC
DECLARE @MAXCol INT = (SELECT MAX(RowNum)FROM #temp1)
,@index INT =1
,@ColNames varchar(4000)=''
,@SQL VARCHAR(MAX)=''
WHILE (@index<=@MAXCol)
BEGIN
SET @ColNames =@ColNames +'MAX(CASE WHEN RowNum = '+LTRIM(STR(@index))+' THEN Job END) as Job'+LTRIM(STR(@index))+','
+'MAX(CASE WHEN RowNum = '+LTRIM(STR(@index))+' THEN EffDate END) as EffDate'+LTRIM(STR(@index))+','
+'MAX(CASE WHEN RowNum = '+LTRIM(STR(@index))+' THEN Dept END) as Dept'+LTRIM(STR(@index))+','
SET @Index=@Index +1
END
SET @ColNames = LEFT(@ColNames,LEN(@ColNames)-1) -- Remove Last Comma
SET @SQL = 'SELECT EmpNo ,'+@ColNames+' FROM #temp1 GROUP BY EmpNo'
EXECUTE (@SQL)
这是SQL Fiddle Demo工作
答案 2 :(得分:1)
您可以执行UNPIVOT
,然后执行PIVOT
数据。这可以静态或动态地完成:
静态版本:
select *
from
(
select empid, col + cast(rn as varchar(10)) colname, value
from
(
select Top 20 empid,
job,
convert(varchar(10), effdate, 101) effdate,
dept,
row_number() over(partition by empid order by effdate) rn
from yourtable
order by empid
) x
unpivot
(
value
for col in (Job, Effdate, Dept)
) u
) x1
pivot
(
min(value)
for colname in([Job1], [EffDate1], [Dept1],
[Job2], [EffDate2], [Dept2],
[Job3], [EffDate3], [Dept3])
)p
动态版本:
DECLARE @colsUnpivot AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX),
@colsPivot as NVARCHAR(MAX),
@colsPivotName as NVARCHAR(MAX)
select @colsUnpivot = stuff((select ','+ quotename(C.name)
from sys.columns as C
where C.object_id = object_id('yourtable') and
C.name not in ('empid')
for xml path('')), 1, 1, '')
select @colsPivot
= STUFF((SELECT ','
+ quotename(c.name + cast(t.rn as varchar(10)))
from
(
select row_number() over(partition by empid order by effdate) rn
from yourtable
) t
cross apply sys.columns as C
where C.object_id = object_id('yourtable') and
C.name not in ('empid')
group by c.name, t.rn
order by t.rn, c.name desc
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query
= 'select *
from
(
select empid, col + cast(rn as varchar(10)) colname, value
from
(
select Top 20 empid,
job,
convert(varchar(10), effdate, 101) effdate,
dept,
row_number() over(partition by empid order by effdate) rn
from yourtable
order by empid
) x
unpivot
(
value
for col in ('+ @colsunpivot +')
) u
) x1
pivot
(
min(value)
for colname in ('+ @colspivot +')
) p'
exec(@query)
答案 3 :(得分:0)
这是使用一系列动态创建的MAX / CASE表达式的一种方法。你也可以用PIVOT做到这一点,但这对我来说更快:
DECLARE @sql NVARCHAR(MAX) = N'SELECT EmpID';
SELECT TOP (20) @sql += N',
Job' + rn + ' = MAX(CASE WHEN rn = ' + rn + ' THEN Job END),
EffDate' + rn + ' = MAX(CASE WHEN rn = ' + rn + ' THEN EffDate END),
Dept' + rn + ' = MAX(CASE WHEN rn = ' + rn + ' THEN Dept END)'
FROM
(
SELECT rn = RTRIM(ROW_NUMBER() OVER (ORDER BY name))
FROM sys.all_objects
) AS x;
SET @sql += ' FROM (SELECT *, rn = ROW_NUMBER() OVER
(PARTITION BY EmpID ORDER BY EffDate) FROM dbo.your_table) AS y
GROUP BY EmpID;';
EXEC sp_executesql @sql;
您可以对此进行调整,以确定任何员工的最大工作变更数,而不是默认为20.您也可以考虑采用相反的方式进行排序 - 当然是员工的最后 20如果工作变化超过20,则工作变更与 first 20相关。