Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/71.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
SQL:将一行拆分为多行(规范化)_Sql_Sql Server_Sql Server 2000_Normalization_Split Function - Fatal编程技术网

SQL:将一行拆分为多行(规范化)

SQL:将一行拆分为多行(规范化),sql,sql-server,sql-server-2000,normalization,split-function,Sql,Sql Server,Sql Server 2000,Normalization,Split Function,我正在从设计拙劣的旧数据库升级到新数据库。在旧数据库中,有一个带有字段Id和商品的表。Id是主键,包含int,而productions包含逗号分隔的列表 表a: id | commodities 1135 | fish,eggs,meat 1127 | flour,oil 在新数据库中,我希望tableB的格式为id,commodity,其中每个商品都是tableA中逗号分隔列表中的单个项目 表B: id | commodity 1135 | fish 1135 | e

我正在从设计拙劣的旧数据库升级到新数据库。在旧数据库中,有一个带有字段Id和商品的表。Id是主键,包含int,而productions包含逗号分隔的列表

表a:

id   | commodities
1135 | fish,eggs,meat    
1127 | flour,oil  
在新数据库中,我希望tableB的格式为id,commodity,其中每个商品都是tableA中逗号分隔列表中的单个项目

表B:

id   | commodity
1135 | fish  
1135 | eggs   
1135 | meat  
1127 | flour  
1127 | oil    
我有一个函数functo,当给定一个id、一个列表和一个分隔符时,它返回一个带有id和item字段的表。如何使用此函数将两个字段从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
ALTER FUNCTION dbo.FUNCTION
(
@id int,
@列出VARCHAR(6000),
@德里姆·瓦尔查尔(5)
)
返回
@解析列表表
(
id int,
项目VARCHAR(6000)
)
作为
开始
声明@item VARCHAR(6000),@Pos INT
设置@List=LTRIM(RTRIM(@List))+@Delim
设置@Pos=CHARINDEX(@Delim,@List,1)
而@Pos>0
开始
设置@item=LTRIM(RTRIM(左(@List,@Pos-1)))
如果@item''
开始
插入@ParsedList(id,项)
值(@id,强制转换(@item为VARCHAR(6000)))
结束
设置@List=RIGHT(@List,LEN(@List)-@Pos)
设置@Pos=CHARINDEX(@Delim,@List,1)
结束
返回
结束

您编写了一个SQL批处理,该批处理在表a中循环,并将函数调用的结果插入表b中

您需要一种在TSQL中拆分和处理字符串的方法,有很多方法可以做到这一点。本文介绍了几乎每种方法的优缺点:

您需要创建一个拆分函数。这就是拆分函数的使用方式:

SELECT
    *
    FROM YourTable                               y
    INNER JOIN dbo.yourSplitFunction(@Parameter) s ON y.ID=s.Value
[我更喜欢使用数字表方法在TSQL中拆分字符串]()但是在SQL Server中拆分字符串的方法有很多,请参见前面的链接,其中解释了每种方法的优缺点

要使Numbers表方法起作用,您需要执行此一次性表设置,它将创建一个包含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)
设置数字表后,创建此拆分函数:

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 
输出:

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

(4 row(s) affected)
id          commodities
----------- -------------
1135        fish
1135        eggs
1135        meat
1127        flour
1127        oil

(5 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
输出:

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

(4 row(s) affected)
id          commodities
----------- -------------
1135        fish
1135        eggs
1135        meat
1127        flour
1127        oil

(5 row(s) affected)
在Conrad Frix关于SQL Server 2000不支持交叉应用的评论后编辑

这将起到同样的作用:

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 + ',') 
插入表格b
(id,商品)
挑选
a、 id,NullIf(子字符串(“,”+a.commodities+“,”,编号,CharIndex(“,”,“,”+a.commodities+”,”,编号)-编号),“”)
从表a
1上的内部联接编号n=1
其中子字符串(','+a.commodities+',',数字-1,1)=','
和CharIndex(“,”,“,”+a.commodities+“,”,number)-number>0

编号以下是我作为评论发布的链接:


称我懒惰,但我会将合并的行从数据库中取出,拆分它们,然后重新插入拆分的行。这种事情对于SQL来说似乎有点不自然…

SSIS有一个非常方便的Unpivot转换(如果您可以使用的话)

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 

你的意思通常被称为规范化(1.精确地说是标准形式)。也许你想把这个添加到你的标题中。这个@Rup:将其作为答案发布。@Rup-谢谢,谢谢,谢谢!请将其作为答案发布,以便我可以投票并接受。好的,当然,完成。我不确定你的功能是什么,所以我不知道这是否回答了整个问题。似乎应该有一种基于集合的方法来实现这一点。。。我不希望唯一的解决办法是批量生产。。。必须有另一种方法…根据那篇文章,你需要一个循环或那个理货台。所以我想就用这批。。。基于集合的独特性在这里似乎失败了……这可以在没有循环的情况下完成,在一个
插入
,查看我的答案。@Conrad Frix,我用一个包含数字表拆分的
插入
语句更新了我的答案,因此不需要
交叉应用
(当然也不需要循环),这是基于中的代码的,如果你是作者,为什么不复制/粘贴,以便堆栈可以看到它,以防下游发生什么情况?