如何在SQL查询中折叠类似的行

时间:2014-04-03 21:05:36

标签: sql

使用MS SQL查询,我想折叠连续行中的冗余数据,以使报表更易于阅读。假设我有以下数据:

ID | Department | Name  | Task
-------------------------------------------------
1    Sales        Mike    Call customer
2    Sales        Mike    Create quote
3    Sales        Sarah   Create order
4    Engineering  Sam     Design prototype
5    Engineering  Sam     Calculate loads
6    Production   Team1   Build parts
7    Production   Team2   Build parts
8    Production   Team1   Assemble parts
9    Accounting   Amy     Invoice job
10   Sales        Mike    Call customer
11   Sales        Mike    Follow up after 30 days

如何从查询中获取以下汇总行:

Sales: Mike (Call customer, Create quote) Sarah (Create order)
Engineering: Sam (Design prototype, Calculate loads)
Production: Team1 (Build parts) Team2 (Build parts) Team1 (Assemble parts)
Accounting: Amy (Invoice job)
Sales: Mike (Call customer, Follow up after 30 days)

基本上,如果前一个部门和名称相同,请将任务添加到逗号分隔的子列表中。如果只有部门相同,则启动一个新的命名子列表,如果部门是新的,则开始一个新行。请注意,第10行和第11行不应包含在第一行中,以便任务顺序不会丢失。

在C#中,这个任务很容易使用循环和/或Linq,但是,我现在需要从SQL查询生成相同的结果。

2 个答案:

答案 0 :(得分:1)

使用递归公用表表达式(working SQLFiddle example

;with RecursivePersonTask( Id, Department, Name, Tasks )
as
( 
  select
   a.Id
   , a.Department
   , a.Name
   , a.Task    
  from
   dbo.Task a
   left outer join dbo.Task b
    on a.Department = b.Department
       and a.Name = b.Name
       and a.Id = b.Id + 1
  where
   b.Id is null

  union all

  select
   t.Id
   , t.Department
   , t.Name
   , rpt.Tasks + ', ' + t.Task
  from
   RecursivePersonTask rpt
   inner join dbo.Task t
    on rpt.Department = t.Department
       and rpt.Name = t.Name
       and rpt.Id = t.Id - 1
)
, CombinedPersonTasks( Id, Department, Name, Tasks )
as
(
  select 
   ROW_NUMBER() over ( order by a.Id )
   , a.Department
   , a.Name
   , '(' + a.Tasks + ')'
  from 
   RecursivePersonTask a
   left outer join RecursivePersonTask b
    on a.Department = b.Department
       and a.Name = b.Name
       and a.Id = b.Id - 1
  where
   b.Id is null
)
, RecursiveDepartmentTasks( Id, Department, Tasks )
as
( 
  select
   a.Id
   , a.Department
   , a.Name + ' ' + a.Tasks
  from
   CombinedPersonTasks a
   left outer join CombinedPersonTasks b
    on a.Department = b.Department
       and a.Id = b.Id + 1
  where
   b.Id is null

  union all

  select
   cpt.Id
   , cpt.Department
   , rdt.Tasks + ' ' + cpt.Name + ' ' + cpt.Tasks
  from
   RecursiveDepartmentTasks rdt
   inner join CombinedPersonTasks cpt
    on rdt.Department = cpt.Department
       and rdt.Id = cpt.Id - 1
)
, CombinedDepartmentTasks( Id, Department, Tasks )
as
(
  select 
   ROW_NUMBER() over ( order by a.Id )
   , a.Department
   , a.Tasks
  from 
   RecursiveDepartmentTasks a
   left outer join RecursiveDepartmentTasks b
    on a.Department = b.Department
       and a.Id = b.Id - 1
  where
   b.Id is null
)

select 
 * 
from
 CombinedDepartmentTasks 
order by 
 Id

答案 1 :(得分:1)

如果您有SQL Server 2012,则可以使用窗口函数的ROWS参数:

with tasks as (
select 1 as id, 'Sales' as dept ,'Mike' as name, 'Call customer' as task union
select 2, 'Sales' ,'Mike', 'Create quote' union
select  3, 'Sales' ,'Sarah', '   Create order' union
select 4, 'Engineering' ,'Sam', '     Design prototype' union
select 5, 'Engineering' ,'Sam', '     Calculate loads' union
select 6, 'Production' ,'Team1', '   Build parts' union
select 7, 'Production' ,'Team2', '   Build parts' union
select 8, 'Production' ,'Team1', '   Assemble parts' union
select 9, 'Accounting' ,'Amy', '     Invoice job' union
select 10, 'Sales' ,'Mike', '    Call customer' union
select 11, 'Sales' ,'Mike', '    Follow up after 30 days' 
)

select max(NewDepartmentRollover) over (order by id rows unbounded preceding) as ColumnToGroupOn , * from (

select 
case when max(dept) over (order by id rows between 1 preceding and 1 preceding) = dept then NULL else id end as NewDepartmentRollover,
*  from tasks
) GroupOn

如果您只有SQL Server 2005或2008,则会执行以下操作:

with tasks as (
select 1 as id, 'Sales' as dept ,'Mike' as name, 'Call customer' as task union
select 2, 'Sales' ,'Mike', 'Create quote' union
select  3, 'Sales' ,'Sarah', '   Create order' union
select 4, 'Engineering' ,'Sam', '     Design prototype' union
select 5, 'Engineering' ,'Sam', '     Calculate loads' union
select 6, 'Production' ,'Team1', '   Build parts' union
select 7, 'Production' ,'Team2', '   Build parts' union
select 8, 'Production' ,'Team1', '   Assemble parts' union
select 9, 'Accounting' ,'Amy', '     Invoice job' union
select 10, 'Sales' ,'Mike', '    Call customer' union
select 11, 'Sales' ,'Mike', '    Follow up after 30 days' 
)


, b  as (
select 
row_number() over (order by tasks.id) as rn,
 tasks.id as lefty
from tasks
left join tasks t3
on t3.id = tasks.id - 1
where tasks.dept <> isnull(t3.dept,'')
)

select tasks.*, lefty as columnToGroupOn from tasks left join (

select b.lefty, isnull(b2.lefty,999)-1 as righty from b
left join 
b  b2 on b.rn = b2.rn - 1
) c
on tasks.id between lefty and righty