3

我正在从设计不良的遗留数据库升级到新数据库。在旧数据库中有tableA,其中包含字段Id和商品。 Id是主键,包含一个int,Commodities包含逗号分隔列表。SQL:将一行分成许多(规范化)

表A:

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

在新的数据库,我想tableB的是在形式的ID,商品,每个商品是从TableA中的逗号分隔的列表中的单个项目。

表B:

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

我有一个函数,泛函,给定一个ID,一个列表,以及一个分隔符时,返回一个表的编号和项目现场。我如何使用这个函数将tableA中的两个字段变成tableB?

(注:我有麻烦搞清楚什么标题这个问题请随时编辑标题,使之更准确地反映问题。!)

下面是函数代码:

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

你是什么意思通常refered为normaliztion(1.正常形态要准确)。也许你想把它添加到你的标题。 – 2010-07-09 17:16:43

+0

数据库?版? – 2010-07-09 17:16:48

+0

SQl Server 2000 – dmr 2010-07-09 17:18:19

回答

2

您可以编写一个SQL批处理循环访问表A,并将函数调用的结果插入到表b中。

+0

似乎应该有一个基于集合的方式来做到这一点...我会讨厌唯一的解决方案是一批......必须有另一种方式... – kralco626 2010-07-09 17:36:59

+0

那么根据该文章,你需要一个循环或那个理货表。所以我想使用批处理...基于集合的idology似乎在这里失败... – kralco626 2010-07-09 17:43:36

+0

这可以在没有循环的情况下完成,在一个'INSERT'中,查看我的答案。 – 2010-07-09 17:52:23

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。

对于数字表的方法来工作,你需要做的这一次表的设置,这将创建一个包含从1到10000行的表Numbers

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 

OUTPUT:

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 

OUTPUT:

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

(5 row(s) affected) 

编辑后有关SQL Server 2000不支持CROSS APPLY康拉德Frix评论

这也将这样做:

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代码。它基本上删除了函数调用,并在主查询中进行拆分(使用类似的Numbers表拆分),因此不需要CROSS APPLY

+1

CROSS APPLY在2000年无法运行 – 2010-07-09 18:47:19

+0

@Conrad Frix,我已经用一个包含Numbers表拆分的单个语句“INSERT”更新了我的答案,所以不需要“CROSS APPLY”(并且肯定没有循环),它基于[@Rup答案中的链接]中的代码(http://stackoverflow.com/questions/3214972/sql-break-up-one-row-into-many-normalization/3215431#3215431) – 2010-07-09 19:04:17

+0

+ 1无循环。 – 2010-07-09 19:17:58

2

叫我懒,但我拉结合行从数据库中,其拆分,然后重新插入分离行。对于SQL来说,这种事情似乎有点不自然......

1

如果你可以使用SSIS,它可以非常方便地使用Unpivot转换。

+1

SSIS是2005年的一个功能。 – 2010-07-09 20:06:11

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