Как справиться со списками и диапазонами, встроенными в столбец? - PullRequest
1 голос
/ 10 февраля 2011

У меня нет контроля над форматом данных, которые я пытаюсь обработать.Конечно, я мог бы использовать язык сценариев для решения следующей проблемы вне базы данных, но я хотел бы избежать этого из-за большого количества данных, с которыми я имею дело, и потому что я хотел бы устранить необходимость ручного управленияшаги.

Короче, у меня есть таблица списков.Список может состоять из одной трехзначной строки, нескольких трехзначных строк, диапазона трехзначных строк, например, 012-018, или нескольких трехзначных строк и диапазонов трехзначных строк.Например:

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?

1 Ответ

3 голосов
/ 10 февраля 2011

Поскольку вы не пометили свой вопрос конкретной СУБД, мне придется ответить в целом.

Сам по себе SQL не обеспечивает базовую операцию, которую вы ищете, которая в основном являетсяразделение строк.Это означает, что вам придется написать свой собственный или использовать один из много , которые были опубликованы в Интернете.

Однако вы немного усложнили вопросы с диапазонами, которыеу вас есть в ваших данных.Это означает, что ваша процедура будет выглядеть примерно так:

  1. Вставьте ваши данные в таблицу temp / memory и выполните итерацию по ней процедурно (или, альтернативно, используйте курсор, чтобы сделать то же самое)
  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
...