2

我有一个数据库,它提供了一个员工 ID、一份工作、一个有效日期和一个部门。如果员工从事过一项以上的工作,他们将获得额外的数据行。我的目标是将与每个员工对应的行压缩为一个。基本上我需要一个查询来从一个看起来像这样的数据库中提取:

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 不会理解我试图调用每个表和字段的命名约定。有没有一种使用 cast 或 concat 命令的方法可以使该过程自动化,因此它只会增加我要求的数字?

4

4 回答 4

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
于 2012-09-05T23:17:49.437 回答
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 with Demo

动态版本:

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 with Demo

于 2012-09-05T23:12:37.173 回答
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 演示工作

http://sqlfiddle.com/#!3/99cea/1

于 2012-09-05T23:06:56.357 回答
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 次更相关,如果他们有超过 20 个。

于 2012-09-05T22:50:26.320 回答