表格规范化(将逗号分隔的字段解析为单个记录)

时间:2011-06-20 22:15:12

标签: sql sql-server tsql sql-server-2008 database-design

我有一张这样的表:

设备

DeviceId   Parts

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

我想创建一个表'Parts',将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中执行此操作吗?

4 个答案:

答案 0 :(得分:6)

- 设置:

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 :(得分:1)

您需要一个Tally表才能在没有游标的情况下完成此操作。

按照说明在此处创建计数表:Tally Tables by Jeff Moden

此脚本会将表放入Temp数据库,因此您可能希望更改“使用DB”语句

然后,您可以运行下面的脚本,将设备和部件的细分插入到临时表中。然后,您应该能够通过部件名称加入零件表(以获取ID)并插入到新的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) = ', '

答案 2 :(得分:0)

查看使用fn_Split从逗号分隔值创建表变量。 然后,您可以使用它来驱动插入。

编辑:实际上,我认为你可能仍然需要一个游标。将这个答案留给fn_Split会有所帮助。

答案 3 :(得分:0)

如果每个设备有最大数量的部件,那么是的,可以在没有光标的情况下完成,但这非常复杂。

实质上,创建一个表(或视图或子查询),其中包含PartID字符串中每个可能索引的DeviceID和一个PartID列。这可以通过使用fn_split或您选择的其他方法使PartID列计算列来实现。从那里你做了这个表的多个自我UNION,在每个PartID列的自UNION中有一个表。 self-UNION中的每个表只有一个PartID列包含在表的查询的选择列表中。