使用 SQL Server 2008 R2 将字符串以逗号分隔成列
Comma separted string into columns using SQL Server 2008 R2
我想使用存储过程将批量值插入 table。
下面是以下 table 调用的 m1
,有两个字段:
create table m1
(
cola varchar(50),
colb varchar(50)
);
程序 m1_test
将值插入 table:
create procedure m1_test
@stringCola varchar(max),
@stringColb varchar(max)
AS
INSERT INTO m1 values(@stringCola,@stringColb);
GO
上述过程适用于插入单个值,如下所示:
---EXECUTE SP
EXECUTE m1_test @stringCola = 'A1',@stringColb = 'Z1';
但是 如果我想在单个字符串中一次插入更多值,如下所示:
---EXECUTE SP
EXECUTE m1_test @stringCola = 'A1,A2,A3',@stringColb = 'Z1,Z2,Z3';
输出应该是:
cola colb
------------
A1 Z1
A2 Z2
A3 Z3
首先您需要创建一个 table 值函数,它将 return 以 table.
形式的逗号分隔值
为函数实现以下代码,我发现 here,对 return 身份列稍作修改,您稍后将根据该列执行 JOIN
以获得A 列的第一个值和 B 列的第一个值 (A1, Z1)
的元组,依此类推第二个、第三个等等。:
CREATE FUNCTION Split (@InputString VARCHAR(8000), @Delimiter VARCHAR(50))
RETURNS @Items TABLE (ID INTEGER IDENTITY(1,1), Item VARCHAR(8000))
AS
BEGIN
IF @Delimiter = ' '
BEGIN
SET @Delimiter = ','
SET @InputString = REPLACE(@InputString, ' ', @Delimiter)
END
IF (@Delimiter IS NULL OR @Delimiter = '')
SET @Delimiter = ','
--INSERT INTO @Items VALUES (@Delimiter) -- Diagnostic
--INSERT INTO @Items VALUES (@InputString) -- Diagnostic
DECLARE @Item VARCHAR(8000)
DECLARE @ItemList VARCHAR(8000)
DECLARE @DelimIndex INT
SET @ItemList = @InputString
SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0)
WHILE (@DelimIndex != 0)
BEGIN
SET @Item = SUBSTRING(@ItemList, 0, @DelimIndex)
INSERT INTO @Items VALUES (@Item)
-- Set @ItemList = @ItemList minus one less item
SET @ItemList = SUBSTRING(@ItemList, @DelimIndex+1, LEN(@ItemList)-@DelimIndex)
SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0)
END -- End WHILE
IF @Item IS NOT NULL -- At least one delimiter was encountered in @InputString
BEGIN
SET @Item = @ItemList
INSERT INTO @Items VALUES (@Item)
END
-- No delimiters were encountered in @InputString, so just return @InputString
ELSE INSERT INTO @Items VALUES (@InputString)
RETURN
END -- End Function
GO
---- Set Permissions
--GRANT SELECT ON Split TO UserRole1
--GRANT SELECT ON Split TO UserRole2
--GO
现在,创建此函数后,将存储过程修改为:
CREATE PROCEDURE m1_test @stringCola VARCHAR(max), @stringColb VARCHAR(max)
AS
INSERT INTO m1
SELECT A.Item, B.Item
FROM Split(@stringColA, ',') A
INNER JOIN Split(@stringColB, ',') B ON A.ID = B.ID
GO
正如我在评论中所说,这是糟糕的设计,但您可以这样做:
INSERT INTO m1 values
SELECT a.d ,
b.d
FROM (SELECT Split.a.value('.', 'VARCHAR(100)') d ,
ROW_NUMBER() OVER ( ORDER BY ( SELECT NULL ) ) rn
FROM (SELECT CAST('<M>' + REPLACE(@stringCola, ',', '</M><M>') + '</M>' AS XML) d) A
CROSS APPLY d.nodes('/M') AS Split ( a )
) a
JOIN
(SELECT Split.a.value('.', 'VARCHAR(100)') d ,
ROW_NUMBER() OVER ( ORDER BY ( SELECT NULL ) ) rn
FROM (SELECT CAST('<M>' + REPLACE(@stringColb, ',', '</M><M>') + '</M>' AS XML) d ) A
CROSS APPLY d.nodes('/M') AS Split ( a )
) b ON a.rn = b.rn
我想使用存储过程将批量值插入 table。
下面是以下 table 调用的 m1
,有两个字段:
create table m1
(
cola varchar(50),
colb varchar(50)
);
程序 m1_test
将值插入 table:
create procedure m1_test
@stringCola varchar(max),
@stringColb varchar(max)
AS
INSERT INTO m1 values(@stringCola,@stringColb);
GO
上述过程适用于插入单个值,如下所示:
---EXECUTE SP
EXECUTE m1_test @stringCola = 'A1',@stringColb = 'Z1';
但是 如果我想在单个字符串中一次插入更多值,如下所示:
---EXECUTE SP
EXECUTE m1_test @stringCola = 'A1,A2,A3',@stringColb = 'Z1,Z2,Z3';
输出应该是:
cola colb
------------
A1 Z1
A2 Z2
A3 Z3
首先您需要创建一个 table 值函数,它将 return 以 table.
形式的逗号分隔值为函数实现以下代码,我发现 here,对 return 身份列稍作修改,您稍后将根据该列执行 JOIN
以获得A 列的第一个值和 B 列的第一个值 (A1, Z1)
的元组,依此类推第二个、第三个等等。:
CREATE FUNCTION Split (@InputString VARCHAR(8000), @Delimiter VARCHAR(50))
RETURNS @Items TABLE (ID INTEGER IDENTITY(1,1), Item VARCHAR(8000))
AS
BEGIN
IF @Delimiter = ' '
BEGIN
SET @Delimiter = ','
SET @InputString = REPLACE(@InputString, ' ', @Delimiter)
END
IF (@Delimiter IS NULL OR @Delimiter = '')
SET @Delimiter = ','
--INSERT INTO @Items VALUES (@Delimiter) -- Diagnostic
--INSERT INTO @Items VALUES (@InputString) -- Diagnostic
DECLARE @Item VARCHAR(8000)
DECLARE @ItemList VARCHAR(8000)
DECLARE @DelimIndex INT
SET @ItemList = @InputString
SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0)
WHILE (@DelimIndex != 0)
BEGIN
SET @Item = SUBSTRING(@ItemList, 0, @DelimIndex)
INSERT INTO @Items VALUES (@Item)
-- Set @ItemList = @ItemList minus one less item
SET @ItemList = SUBSTRING(@ItemList, @DelimIndex+1, LEN(@ItemList)-@DelimIndex)
SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0)
END -- End WHILE
IF @Item IS NOT NULL -- At least one delimiter was encountered in @InputString
BEGIN
SET @Item = @ItemList
INSERT INTO @Items VALUES (@Item)
END
-- No delimiters were encountered in @InputString, so just return @InputString
ELSE INSERT INTO @Items VALUES (@InputString)
RETURN
END -- End Function
GO
---- Set Permissions
--GRANT SELECT ON Split TO UserRole1
--GRANT SELECT ON Split TO UserRole2
--GO
现在,创建此函数后,将存储过程修改为:
CREATE PROCEDURE m1_test @stringCola VARCHAR(max), @stringColb VARCHAR(max)
AS
INSERT INTO m1
SELECT A.Item, B.Item
FROM Split(@stringColA, ',') A
INNER JOIN Split(@stringColB, ',') B ON A.ID = B.ID
GO
正如我在评论中所说,这是糟糕的设计,但您可以这样做:
INSERT INTO m1 values
SELECT a.d ,
b.d
FROM (SELECT Split.a.value('.', 'VARCHAR(100)') d ,
ROW_NUMBER() OVER ( ORDER BY ( SELECT NULL ) ) rn
FROM (SELECT CAST('<M>' + REPLACE(@stringCola, ',', '</M><M>') + '</M>' AS XML) d) A
CROSS APPLY d.nodes('/M') AS Split ( a )
) a
JOIN
(SELECT Split.a.value('.', 'VARCHAR(100)') d ,
ROW_NUMBER() OVER ( ORDER BY ( SELECT NULL ) ) rn
FROM (SELECT CAST('<M>' + REPLACE(@stringColb, ',', '</M><M>') + '</M>' AS XML) d ) A
CROSS APPLY d.nodes('/M') AS Split ( a )
) b ON a.rn = b.rn