Нормализация таблицы (разделяет поля, разделенные запятыми, на отдельные записи)

У меня есть таблица вроде этого:

Устройство

DeviceId   Parts

1          Part1, Part2, Part3
2          Part2, Part3, Part4
3          Part1

Я хотел бы создать таблицу "Parts", экспортировать данные из столбца Parts в новую таблицу. После этого я закрою столбец "Детали"

Ожидаемый результат

<сильные > Части

PartId PartName

  1      Part1
  2      Part2
  3      Part3
  4      Part4

DevicePart

DeviceId PartId

  1      1
  1      2
  1      3
  2      2
  2      3
  2      4
  3      1

Могу ли я сделать это в SQL Server 2008 без использования курсоров?

Ответ 1

- Настройка:

declare @Device table(DeviceId int primary key, Parts varchar(1000))
declare @Part table(PartId int identity(1,1) primary key, PartName varchar(100))
declare @DevicePart table(DeviceId int, PartId int)

insert @Device
values
    (1, 'Part1, Part2, Part3'),
    (2, 'Part2, Part3, Part4'),
    (3, 'Part1')

- Script:

declare @DevicePartTemp table(DeviceId int, PartName varchar(100))

insert @DevicePartTemp
select DeviceId, ltrim(x.value('.', 'varchar(100)'))
from
(
    select DeviceId, cast('<x>' + replace(Parts, ',', '</x><x>') + '</x>' as xml) XmlColumn
    from @Device
)tt
cross apply
    XmlColumn.nodes('x') as Nodes(x)


insert @Part
select distinct PartName
from @DevicePartTemp

insert @DevicePart
select tmp.DeviceId, prt.PartId
from @DevicePartTemp tmp 
    join @Part prt on
        prt.PartName = tmp.PartName

- Результат:

select *
from @Part

PartId      PartName
----------- ---------
1           Part1
2           Part2
3           Part3
4           Part4


select *
from @DevicePart

DeviceId    PartId
----------- -----------
1           1
1           2
1           3
2           2
2           3
2           4
3           1   

Ответ 2

Вам понадобится таблица Tally, чтобы выполнить это без курсора.

Следуйте инструкциям, чтобы создать таблицу таблиц: Таблицы Tally by Jeff Moden

Этот script помещает таблицу в вашу базу данных Temp, поэтому вы, вероятно, захотите изменить оператор "Использовать БД"

Затем вы можете запустить script ниже, чтобы вставить разбивку устройств и частей в таблицу temp. Затем вы можете присоединиться к своей части таблицы именем части (получить идентификатор) и вставить в новую таблицу DevicePart.

select *, 
--substring(d.parts, 1, t.n)
substring(d.parts, t.n, charindex(', ', d.parts + ', ',t.n) - t.n) 'Part'
into #devicesparts
from device d
cross join tally t
where t.n < (select max(len(parts))+ 1 from device)
and substring(', ' + d.parts, t.n, 1) = ', '

Ответ 3

Посмотрите на использование fn_Split, чтобы создать переменную таблицы из значений, разделенных запятыми. Затем вы можете использовать это для вставки вашей вставки.

EDIT: На самом деле, я думаю, вам может понадобиться курсор. Если оставить этот ответ, то fn_Split поможет.

Ответ 4

Если на устройство имеется максимальное количество частей, тогда да, это можно сделать без курсора, но это довольно сложно.

По существу, создайте таблицу (или представление или подзапрос), которая имеет идентификатор DeviceID и один столбец PartID для каждого возможного индекса в строке PartID. Это можно сделать, сделав колонки столбцов PartID, используя fn_split или другой метод по вашему выбору. Оттуда вы делаете множественный самосоединение этой таблицы с одной таблицей в самосоединении для каждого столбца PartID. Каждая таблица в self-UNION имеет только один из столбцов PartID, включенных в список выбора запроса для таблицы.