Нормализация таблиц (Разобрать поля через запятую в отдельные записи) - PullRequest
5 голосов
/ 21 июня 2011

У меня есть такая таблица:

Устройство

DeviceId   Parts

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

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

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

части

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 без использования курсоров?

Ответы [ 4 ]

6 голосов
/ 21 июня 2011

- Настройка:

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')

- Автор сценария:

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   
1 голос
/ 21 июня 2011

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

Следуйте инструкциям, чтобы создать таблицу подсчета здесь: Таблицы подсчета Джеффа Модена

Этот скрипт поместит таблицу в вашу временную базу данных, так что вы, вероятно, захотите изменить оператор «Использовать БД»

Затем вы можете запустить приведенный ниже скрипт, чтобы вставить разбивку «Устройства и детали» во временную таблицу. После этого вы сможете присоединиться к таблице деталей по имени детали (чтобы получить идентификатор) и вставить ее в новую таблицу 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) = ', '
0 голосов
/ 21 июня 2011

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

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

0 голосов
/ 21 июня 2011

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

РЕДАКТИРОВАТЬ: На самом деле, я думаю, вам все еще может понадобиться курсор.Если оставить этот ответ, то поможет fn_Split.

...