3

Я нахожусь в середине обновления из плохо разработанной устаревшей базы данных в новую базу данных. В старой базе данных есть таблицаA с полями Id и Commodities. Идентификатор является основным ключом и содержит int и Commodities содержит список с разделителями-запятыми.SQL: разбить одну строку на многие (нормализация)

TableA:

id | commodities 
1135 | fish,eggs,meat  
1127 | flour,oil 

В новой базе данных, я хочу TableB быть в виде идентификатора, товарное, где каждый товар есть один элемент из списка разделенных запятыми в TABLEA.

TableB:

id | commodity 
1135 | fish 
1135 | eggs 
1135 | meat 
1127 | flour 
1127 | oil  

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

(Примечание: У меня была проблема, выясняя, что титул этот вопрос, пожалуйста, не стесняйтесь изменить название, чтобы сделать его более точно отразить этот вопрос.!)

Вот код функции:

ALTER FUNCTION dbo.functionA 
(
@id int, 
@List VARCHAR(6000), 
@Delim varchar(5) 
) 
RETURNS 
@ParsedList TABLE 
(
id int, 
item VARCHAR(6000) 
) 
AS 
BEGIN 
DECLARE @item VARCHAR(6000), @Pos INT 
SET @List = LTRIM(RTRIM(@List))+ @Delim 
SET @Pos = CHARINDEX(@Delim, @List, 1) 
WHILE @Pos > 0 
BEGIN 
SET @item = LTRIM(RTRIM(LEFT(@List, @Pos - 1))) 
IF @item <> '' 
BEGIN 
INSERT INTO @ParsedList (id, item) 
VALUES (@id, CAST(@item AS VARCHAR(6000))) 
END 
SET @List = RIGHT(@List, LEN(@List) - @Pos) 
SET @Pos = CHARINDEX(@Delim, @List, 1) 
END 
RETURN 
END 
+0

Что вы имеете в виду, обычно упоминается как normaliztion (1 нормальной форме, если быть точным). Возможно, вы хотите добавить это в свой заголовок. –

+0

База данных? Версия? –

+0

SQl Server 2000 – dmr

ответ

3

Вот ссылка я разместил в качестве комментария:

http://www.sqlteam.com/article/parsing-csv-values-into-multiple-rows

+1

+1, хорошая ссылка, код работает на SQL Server 2000 –

+0

Если вы являетесь автором, почему бы не копировать/вставлять, чтобы укладчики могли видеть его в случае, если что-то происходит по течению? –

2

Вы пишете пакет SQL, который проходит через таблицу A и вставляет в таблицу b результаты вызова функции.

+0

Кажется, что должен быть установленный способ сделать это ... Я бы не хотел, чтобы единственным решением было партию ... Должен быть другой способ ... – kralco626

+0

Ну в соответствии с этой статьей вам нужен цикл или это таблица таблиц. Так что я думаю, что просто использую пакет ... Идиология, основанная на наборе, кажется, терпит неудачу здесь ... – kralco626

+0

это можно сделать без цикла, в одном «INSERT», см. Мой ответ. –

3

Вам нужен способ разделить и обработать строку в TSQL, есть много способов сделать это. В данной статье рассматривается плюсы и минусы просто о каждом методе:

Arrays and Lists in SQL Server 2000 and Earlier

Вам необходимо создать раздельную функцию. Это как функция разделения может быть использовано:

SELECT 
    * 
    FROM YourTable        y 
    INNER JOIN dbo.yourSplitFunction(@Parameter) s ON y.ID=s.Value 

[Я предпочитаю подход номер таблицы, чтобы разбить строку в TSQL] (Arrays and Lists in SQL Server 2000 and Earlier), но существует множество способов разбиения строк в SQL Server, см предыдущей ссылки , что объясняет PRO и CON каждого.

Для метода Числа таблицы для работы, вы должны сделать это один раз настройки таблицы, которая будет создавать таблицу Numbers, которая содержит строки от 1 до 10000:

SELECT TOP 10000 IDENTITY(int,1,1) AS Number 
    INTO Numbers 
    FROM sys.objects s1 
    CROSS JOIN sys.objects s2 
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number) 

После того как таблица Numbers устанавливается , создать разбитую функцию:

CREATE FUNCTION inline_split_me (@SplitOn char(1),@param varchar(7998)) RETURNS TABLE AS 
    RETURN(SELECT substring(@SplitOn + @param + ',', Number + 1, 
        charindex(@SplitOn, @SplitOn + @param + @SplitOn, Number + 1) - Number - 1) 
       AS Value 
      FROM Numbers 
      WHERE Number <= len(@SplitOn + @param + @SplitOn) - 1 
      AND substring(@SplitOn + @param + @SplitOn, Number, 1) = @SplitOn) 

GO 

Теперь вы можете легко разделить строку CSV в таблицу и присоединиться к ней на:

select * from dbo.inline_split_me(';','1;22;333;4444;;') where LEN(Value)>0 

ВЫВОД:

Value 
---------------------- 
1 
22 
333 
4444 

(4 row(s) affected) 

сделать вам новое применение таблицы это:

--set up tables: 
create table TableA (id int, commodities varchar(8000)) 
INSERT TableA VALUES (1135,'fish,eggs,meat') 
INSERT TableA VALUES (1127,'flour,oil') 

Create table TableB (id int, commodities varchar(8000)) 

--populate TableB 
INSERT TableB 
    (id, commodities) 
SELECT 
    a.id,c.value 
    FROM TableA a 
     CROSS APPLY dbo.inline_split_me(',',a.commodities) c 

--show tableB contents: 
select * from TableB 

ВЫВОД:

id   commodities 
----------- ------------- 
1135  fish 
1135  eggs 
1135  meat 
1127  flour 
1127  oil 

(5 row(s) affected) 

EDIT после Conrad Frix комментарий о SQL Server 2000 не поддерживает CROSS APPLY

это будет делать то же самое:

INSERT TableB 
     (id, commodities) 
    SELECT 
     a.id,NullIf(SubString(',' + a.commodities + ',' , number , CharIndex(',' , ',' + a.commodities + ',' , number) - number) , '') 
     FROM TableA   a 
      INNER JOIN Numbers n ON 1=1 
     WHERE SubString(',' + a.commodities + ',' , number - 1, 1) = ',' 
     AND CharIndex(',' , ',' + a.commodities + ',' , number) - number > 0 
     AND number <= Len(',' + a.commodities + ',') 

и основан на коде от the link in the answer by @Rup. Он в основном удаляет вызов функции и выполняет разделение в основном запросе (используя аналогичную таблицу split numbers), поэтому нет необходимости в CROSS APPLY.

+1

CROSS APPLY не будет работать в 2000 году –

+0

@Conrad Frix, я обновил свой ответ с помощью одного оператора 'INSERT', который включает в себя таблицу Numbers split, поэтому не требуется« CROSS APPLY »(и, конечно, нет циклов), что основан на коде в [ссылке в ответе @Rup] (http://stackoverflow.com/questions/3214972/sql-break-up-one-row-into-many-normalization/3215431#3215431) –

+0

+ 1 без петель. –

2

Позвоните мне лень, но я бы тянуть объединенные строки из базы данных, разделить их, а затем снова вставьте разрезные строки. Такая вещь кажется неестественной для SQL ...

1

SSIS имеет довольно удобное преобразование Unpivot, если оно доступно для вас.

+1

SSIS - это функция 2005 года. –

1
create table Project (ProjectId int, Description varchar(50)); 
insert into Project values (1, 'Chase tail, change directions'); 
insert into Project values (2, 'ping-pong ball in clothes dryer'); 

create table ProjectResource (ProjectId int, ResourceId int, Name varchar(15)); 
insert into ProjectResource values (1, 1, 'Adam'); 
insert into ProjectResource values (1, 2, 'Kerry'); 
insert into ProjectResource values (1, 3, 'Tom'); 
insert into ProjectResource values (2, 4, 'David'); 
insert into ProjectResource values (2, 5, 'Jeff'); 

-- a bit of SQL magic involving XML and voila 
SELECT *, 
    (SELECT Name + ' ' AS [text()] 
    FROM ProjectResource pr 
    WHERE pr.ProjectId = p.ProjectId 
    FOR XML PATH ('')) AS ResourceList 
FROM Project p 

Выход из этого будет:

ProjectId Description      ResourceList 
1   Chase tail, change directions  Adam Kerry Tom 
2   ping-pong ball in clothes dryer David Jeff