如何进行递归查询

时间:2012-10-02 01:52:40

标签: sql sql-server database tsql recursive-query

我有一个如下所示的数据库表:

EVENT_ID TEXT_FRO TEXT_TO
55001              05
55001    05        10
55001    10        15
55001    15        20
55001    20        30
56215    06        11
56215    11        22

我需要编写一个查询(或SP)来生成一个结果集,列出每个不同event_ID的所有动作,如下所示:

Event ID Movements
55001    05 10 15 20 30
56215    06 11 22

我该怎么做?

*编辑以简化示例

2 个答案:

答案 0 :(得分:1)

如果使用函数和光标,这是非常简单的:

use tempdb
go

create table tmp (
  EVENT_ID int, 
  TEXT_FRO varchar(10), 
  TEXT_TO varchar(10)
)
go

insert into tmp values
(55001, NULL, '05'),
(55001, '05', '26'),
(55001, '26', '28'),
(55001, '28', '27'),
(55001, '27', '26'),
(55001, '26', '27'),
(55001, '27', '28'),
(55001, '28', '30'),
(55001, '30', '40'),
(56215, '06', '11'),
(56215, '11', '22')
go

您必须创建一个函数来组合连接字符串:

create function fnConcat (@id int) returns varchar(255) as
begin
  declare @rtn varchar(255) = '', @fro varchar(10), @to varchar(10), @cnt int = 1

  declare cr cursor local for
    select TEXT_FRO, TEXT_TO
    from   tmp
    where  EVENT_ID = @id

  open cr
  fetch next from cr into @fro, @to

  while @@fetch_status = 0
  begin
    if @cnt = 1 and @fro is not null
      set @rtn = @rtn + @fro + ' '

    set @rtn = @rtn + @to + ' '

    set @cnt = @cnt + 1
    fetch next from cr into @fro, @to
  end

  close cr
  deallocate cr

  set @rtn = left(@rtn, datalength(@rtn) - 1)

  return @rtn
end
go

如果您只为每个唯一的EVENT_ID调用一个时间,效率更高,因此我们在子查询中选择distinct EVENT_ID:

select x.EVENT_ID as [Event ID], dbo.fnConcat(x.EVENT_ID) as Movements
from (
  select distinct EVENT_ID
  from tmp
) as x
go

然后清理:

drop table tmp
go
drop function fnConcat
go

结果如下:

Event ID    Movements
----------- ---------------------------
55001       05 26 28 27 26 27 28 30 40
56215       06 11 22

答案 1 :(得分:1)

您可以使用递归公用表表达式执行此操作。

出于测试目的,我在测试表中添加了一列,用于标识事件的顺序,以便能够识别第一个:

create table movements 
(
  event_id   INTEGER,
  text_fro   VARCHAR(10),
  text_to    VARCHAR(10),
  sort_ord   INTEGER
);

insert into movements (event_id, text_fro, text_to, sort_ord)
values
(55001,null,'05',1),
(55001,'05','10',2),
(55001,'10','15',3),
(55001,'15','20',4),
(55001,'20','30',5),
(56215,'06','11',1),
(56215,'11','22',2)
;

with mvt as (
   select m1.event_id as root_id,
          m1.event_id,
          cast(coalesce(m1.text_fro,'') + ' ' + m1.text_to as varchar(8000)) as path,
          m1.text_fro,
          m1.text_to,
          m1.sort_ord
   from movements m1
   where m1.text_fro is null
   or m1.sort_ord = (select min(sort_ord) from movements m2 where m1.event_id = m2.event_id)

   union all 

   select p.root_id,
          c.event_id,
          p.path + ' ' + c.text_to,
          c.text_fro,
          c.text_to,
          c.sort_ord
   from movements c
     join mvt p on p.text_to = c.text_fro
)
select root_id, max(ltrim(path)) as movements
from mvt
group by root_id;

这是SQLFiddle demo

我不知道如何处理事件循环中的循环。