繁体   English   中英

使用WHILE LOOP在SQL Server 2008中迭代SELECT语句

[英]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演示的工作方式

http://sqlfiddle.com/#!3/99cea/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

参见带有演示的SQL Fiddle

动态版本:

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)

参见带有演示的SQL Fiddle

这是使用一系列动态创建的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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM