2011-02-10 27 views
1

我无法控制我正在处理的数据格式。当然,我可以使用脚本语言来处理数据库之外的以下问题,但是我想避免这种情况,因为我处理的数据量很大,因为我想消除手动操作的必要性脚步。如何处理列中嵌入的列表和范围?

总之,我有一张表。列表可以由单个3位字符串,多于一个3位字符串,一系列3位字符串(例如, 012-018或3位字符串的数量和3位数字串的范围。例如:

drop table list; 
drop table lists; 

create table lists (id varchar, vals varchar); 

insert into lists values('A', '001,003-005'); 
insert into lists values('B', '008-007'); 
insert into lists values('C', '010, 011, 012'); 
insert into lists values('D', '011-013, 016-018, 020'); 

我知道我知道

我想这变成如下表:

create table list (id varchar, val varchar); 
A 001 
A 003 
A 004 
A 005 
B 008 
B 007 
C 010 
C 011 
C 012 
D 011 
D 012 
D 013 
D 016 
D 017 
D 018 
D 020

有没有办法在SQL做到这一点?

+0

您正在使用哪种RDBMS? – 2011-02-10 19:21:33

+0

我实际上是在SAS的`PROC SQL`中执行此操作。 – 2011-02-10 19:26:32

回答

3

既然你没有用特定的RDBMS标记你的问题,我将不得不一般回答。

SQL本身不提供您正在查找的基本操作,这基本上是一个字符串分割。这意味着你必须自己写,或者使用在线发布的许多之一。

尽管如此,您已将事情复杂化了一些,但数据范围有限。这意味着你的程序将是这个样子:

  1. 插入您的数据到一个临时/内存表和程序遍历它(或者,使用游标做相同的)
  2. 对于记录集中的每条记录,提取非标准化字符串数据并将其分割为','
  3. 对于拆分数据中的每个元素,您必须分割,,'-'(对于非范围元素,应返回单个结果)。
  4. 如果您的第二次拆分(在'-'上)会产生一个结果,那么这是一条记录,您可以将其插入到最终目的地。如果产生两个结果,那么它是一个范围,你必须从一开始到结束(使用元素1和分裂2)迭代和记录插入到您的最终目的地后评论

编辑

不幸的是,我不熟悉PROC SQL或SAS,所以我不能提供具体的解决方案。我可以在SQL Server T-SQL中发布以下内容,希望能够让你开始。

declare @results table (idx int identity(1, 1), id varchar(5), data varchar(max)) 
declare @elements table (idx int identity(1, 1), element varchar(25)) 
declare @range table (idx int identity(1, 1), element varchar(25)) 

insert into @results (id, data) 
select 
    your_id, 
    your_data 

from your_source 

declare @i int 
declare @cnt int 

declare @j int 
declare @cnt2 int 

declare @element varchar(25) 

declare @first int 
declare @second int 

declare @start int 
declare @end int 

declare @id varchar(5) 
declare @data varchar(max) 

select @i = min(idx) - 1, @cnt = max(idx) from @results 

while @i < @cnt 
begin 
    select @i = @i + 1 

    select @id = id, @data = data from @results where idx = @i 

    delete @elements 

    insert into @elements (element) 
    select 
     element 

    from split(@data, ',') 

    select @j = min(idx) - 1, @cnt2 = max(idx) from @elements 

    while @j < @cnt2 
    begin 
     select @j = @j + 1 

     select @element = element from @elements where idx = @j 

     delete @range 

     insert into @range (element) 
     select 
      element 

     from split(@element, '-') 

     select @first = min(idx), @second = max(idx) from @range 

     if @first = @second --single element 
      insert into final_destination (id, value) 
      select 
       @id, 
       element 

      from @range 
     else if @second - @first = 1 -- two elements, as desired 
     begin 
      select @start = convert(int, element) - 1 from @range where idx = @first 
      select @end = convert(int, element) from @range where idx = @second 

      while @start < @end 
      begin 
       select @start = @start + 1 

       insert into final_destination (id, value) 
       values (@id, @start) 
      end 
     end 
     else -- error condition, bad input 
    end 
end