2011-02-10 40 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