![](/img/trans.png)
[英]How to retrieve records using select statement in while loop in SQL Server?
[英]Iterating a SELECT statement in SQL Server 2008 using a WHILE LOOP
我有一个数据库,提供了一个employeeID,一份工作,一个有效的日期和一个部门。 如果一名员工完成了一项以上的工作,那么他们将拥有另一行数据。 我的目标是将与每个员工对应的行压缩为一个。 基本上我需要一个查询,该查询是从数据库中获取的,如下所示:
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无法理解我试图调用每个表和字段的命名约定。 有没有一种方法可以使用强制转换或concat命令来使该过程自动化,从而使它只增加我要求的数字?
首先,让我掩盖一下,这不是直接回答问题。 但是,由于代码块大,它也不适合注释,而且我认为它确实为问题增加了价值。 所以就这样...
动态列数很少是一个好的解决方案。 如果选择使用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
这是解决方案。无论为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演示的工作方式
您可以先进行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)
这是使用一系列动态创建的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次更重要,如果他们已经超过20。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.