2012-09-05 83 views
2

我有一个数据库,让一个雇员,工作,一个EFFECTIVEDATE和部门。如果员工工作多于一份工作,他们将获得额外的一行数据。我的目标是将与每个员工相对应的行压缩成一个。基本上,我需要一个查询到,从一个数据库,看起来像这样拉:迭代在SQL Server 2008中的SELECT语句中使用while循环

要输出这样
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 
[email protected] 
, [email protected] AS [email protected] 
, [email protected] AS [email protected] 
, [email protected] AS [email protected] 
, [email protected] AS [email protected] 
FROM #temp1 AS [email protected] 
LEFT JOIN #[email protected] AS [email protected] 
ON [email protected] = [email protected] AND [email protected] = @FlagPlus 
WHERE JobHist.RowNum = 1 
SET @Flag = (@Flag + 1) 
SET @FlagPlus = (@FlagPlus + 1) 
END 

我知道这可能是行不通的,因为SQL不理解的命名约定我想打电话给每个表和字段。有没有使用强制或CONCAT命令所以它只是增加在那里,我要求它的数字我可以自动处理的方法吗?

+1

我认为你需要退后一步,试图解决更大的问题,而不是尝试解决循环。你究竟在做什么*试图做什么?为什么你需要一个#temp表?这看起来像一个非常简单的数据透视查询(或者您可以在客户端上转置的东西)。 –

+0

你想要的实际上是一个动态数据透视表,它需要动态的sql – ClearLogic

+0

@AaronBetrand我需要修改这个表格来查询我正在构建的另一个查询。我需要知道在特定日期某个部门工作的人员。有时候工作会改变,但部门不会。我的下一个查询需要按顺序比较作业。它正在建立在Cognos。将dept1与dept2进行比较要容易得多,然后在给定的日期查看是否在dept1中使用,如果所有条目都是这样旋转的。不能在客户端进行转置,因为它只是将要交付的更大报表的一小部分。 –

回答

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

1

这里是solution.No无论有多少工作变动的电磁脉冲会转动所有这些 如果你想[透视只有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<[email protected]) 
    BEGIN 
    SET @ColNames [email protected] +'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 @[email protected] +1   
END 
SET @ColNames = LEFT(@ColNames,LEN(@ColNames)-1) -- Remove Last Comma 

SET @SQL = 'SELECT EmpNo ,'[email protected]+' FROM #temp1 GROUP BY EmpNo' 

EXECUTE (@SQL) 

下面是SQL小提琴演示工作

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

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

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