【发布时间】:2017-11-07 04:01:08
【问题描述】:
我有一张如下图所示的表格
是否可以将上述表格数据分行插入到表格中?
我尝试对每一列使用拆分功能,并将每一列的结果存储在一个临时表中。我不知道如何根据 id 将所有这些行和列组合到新表中。任何帮助或建议都会有所帮助。
【问题讨论】:
-
看我的回答,希望对你有帮助。
标签: sql sql-server sql-server-2008 sql-server-2008-r2
我有一张如下图所示的表格
是否可以将上述表格数据分行插入到表格中?
我尝试对每一列使用拆分功能,并将每一列的结果存储在一个临时表中。我不知道如何根据 id 将所有这些行和列组合到新表中。任何帮助或建议都会有所帮助。
【问题讨论】:
标签: sql sql-server sql-server-2008 sql-server-2008-r2
试试这个答案。希望对您有所帮助。
DECLARE @Table TABLE(ID INT, NAME VARCHAR(10),TITLE VARCHAR(10))
INSERT INTO @Table VALUES (1,';a;b;c',';12;13;14')
DECLARE @ID INT=1
SELECT @ID ID,Items,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) RN1 INTO #T1 FROM dbo.split((SELECT NAME FROM @Table WHERE id=@ID),';')
SELECT @ID ID,Items,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) RN2 INTO #T2 FROM dbo.split((SELECT TITLE FROM @Table WHERE id=@ID),';')
SELECT T1.ID,T1.Items NAME,T2.Items TITLE
FROM #T1 T1 INNER JOIN #T2 T2 ON T1.RN1=T2.RN2
DROP TABLE #T1
DROP TABLE #T2
如果你想要所有的值,你可以试试WHILE这样的循环方法。
DECLARE @Table TABLE(ID INT, NAME VARCHAR(10),TITLE VARCHAR(10))
INSERT INTO @Table VALUES (1,';a;b;c',';12;13;14'),(2,';c;f;u',';67;56;34'),(3,';l;k;m',';90;70;60')
DECLARE @MinID INT,@MaxID INT
SELECT @MinID=MIN(ID),@MaxID=MAX(ID) FROM @Table
CREATE TABLE #T1(ID INT,Items VARCHAR(10),RN1 INT)
CREATE TABLE #T2(ID INT,Items VARCHAR(10),RN2 INT)
WHILE @MinID<=@MaxID
BEGIN
INSERT INTO #T1
SELECT @MinID ID,Items,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) RN1
FROM dbo.split((SELECT NAME FROM @Table WHERE id=@MinID),';')
INSERT INTO #T2
SELECT @MinID ID,Items,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) RN2
FROM dbo.split((SELECT TITLE FROM @Table WHERE id=@MinID),';')
SET @MinID=@MinID+1
END
SELECT T1.ID,T1.Items NAME,T2.Items TITLE
FROM #T1 T1 INNER JOIN #T2 T2 ON T1.ID=T2.ID AND T1.RN1=T2.RN2
DROP TABLE #T1
DROP TABLE #T2
这将产生你想要的结果:
ID NAME TITLE
----------- ---------- ----------
1 a 12
1 b 13
1 c 14
2 c 67
2 f 56
2 u 34
3 l 90
3 k 70
3 m 60
这里是拆分函数,我是用来拆分字符串的:
CREATE FUNCTION [dbo].[Split]
(@String VARCHAR (max), @Delimiter CHAR (1))
RETURNS
@temptable TABLE (
[items] VARCHAR (max) COLLATE SQL_Latin1_General_CP1_CI_AS NULL)
AS
begin
declare @idx int
declare @slice varchar(max)
select @idx = 1
if len(@String)<1 or @String is null return
while @idx!= 0
begin
set @idx = charindex(@Delimiter,@String)
if @idx!=0
set @slice = left(@String,@idx - 1)
else
set @slice = @String
if(len(@slice)>0)
insert into @temptable(Items) values(@slice)
set @String = right(@String,len(@String) - @idx)
if len(@String) = 0 break
end
return
end
【讨论】:
这是CTE 的另一种方法,在XML 节点的帮助下
不需要创建任何函数。
WITH cte AS (
SELECT ID,
split.a.value('.', 'NVARCHAR(MAX)') [name],
ROW_NUMBER() OVER(ORDER BY ( SELECT 1)) RN
FROM
(
SELECT ID,
CAST('<A>'+REPLACE(name, ';', '</A><A>')+'</A>' AS XML) AS [name]
FROM <table_name>
) a
CROSS APPLY name.nodes('/A') AS split(a)),
CTE1 AS (
SELECT ID,
split.a.value('.', 'NVARCHAR(MAX)') [title],
ROW_NUMBER() OVER(ORDER BY ( SELECT 1 )) RN
FROM
(
SELECT ID,
CAST('<A>'+REPLACE(title, ';', '</A><A>')+'</A>' AS XML) AS [title]
FROM <table_name>
) aa
CROSS APPLY title.nodes('/A') AS split(a))
SELECT C.ID, C.name, C1.title FROM CTE C
JOIN CTE1 C1 ON C1.RN = C.RN
WHERE C.name != '' AND C1.title != '';
结果:
ID name title
1 a 12
1 b 13
1 s 45
2 c 67
2 f 56
2 u 34
3 l 90
3 k 70
3 m 60
【讨论】:
试试下面的方法..这也可以节省时间和内存!
此 T-SQL 块依赖于 dbo.SplitString 函数 ..
T1 是我的源表
T2 是我的目标表
DECLARE @c_s AS CURSOR;
DECLARE @id INT;
DECLARE @name VARCHAR(1000);
DECLARE @value VARCHAR(1000);
SET @c_s = CURSOR FOR SELECT * FROM T1;
OPEN @c_s;
FETCH @c_s INTO @id, @name, @value
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO T2
SELECT @id
, a.Value NAME
, b.value value
FROM dbo.SplitString(@name, ';') a
INNER JOIN dbo.SplitString(@value, ';') b
ON a.OrdinalPosition = b.OrdinalPosition
FETCH NEXT FROM @c_s INTO @id, @name, @value
END
这里是 dbo.SplitString
CREATE FUNCTION [dbo].[SplitString](@givenString VARCHAR(8000) , @separator VARCHAR(100))
RETURNS TABLE AS
RETURN (
WITH data([start], [end]) AS (
SELECT 0 AS [start]
, CHARINDEX(@separator, @givenString) AS [end]
UNION ALL
SELECT [end] + 1
, CHARINDEX(@separator, @givenString, [end] + 1)
FROM data
WHERE [end] > 0
)
SELECT ROW_NUMBER() OVER (
ORDER BY OrdinalPosition
) OrdinalPosition
, RTRIM(LTRIM(Value)) Value
FROM (
SELECT ROW_NUMBER() OVER (
ORDER BY [start]
) OrdinalPosition
, SUBSTRING(@givenString, [start], COALESCE(NULLIF([end], 0), len(@givenString) + 1) - [start]) Value
FROM data
) r
WHERE RTRIM(Value) <> ''
AND Value IS NOT NULL
)
【讨论】:
您可以通过编写一个表值函数来实现此目的,该函数将根据您的要求拆分字符串。创建此对象后,您可以使用第二个代码 sn-p 中的 T-SQL 来获取最终所需的表。
这个表值函数的定义如下。只需将其复制并粘贴到 SSMS 中,然后针对您的数据库运行即可。
拆分字符串函数
-- =============================================
-- Author: B Vidhya
-- Create date: Nov 7, 2017
-- Description: Splits a string and returns a table
-- =============================================
CREATE FUNCTION [dbo].[SplitAString]
(
@string NVARCHAR(MAX),
@delimiter CHAR(1)
)
RETURNS @splitTable TABLE (
ItemNumber INT IDENTITY(1,1),
Item NVARCHAR(1000)
)
AS
BEGIN
DECLARE @startIndex INT,@endIndex INT
SET @startIndex = 1
IF SUBSTRING(@string, LEN(@string) - 1, LEN(@string)) <> @delimiter
BEGIN
SET @string = @string + @delimiter
END
WHILE CHARINDEX(@delimiter, @string) > 0
BEGIN
SET @endIndex = CHARINDEX(@delimiter, @string)
INSERT INTO @splitTable(Item)
SELECT SUBSTRING(@string, @startIndex, @endIndex - 1)
SET @string = SUBSTRING(@string, @endIndex + 1, LEN(@string))
END
RETURN
END
GO
在下面的 T-SQL 中,我调用了原始表 StackOverflowTable1,您可以将这个表名替换为您的实际表名。另外,我将最后一行插入到表变量中。如果您想插入自定义表,那么您可以在 WHILE 循环的 END 之后对表执行 INSERT。
T-SQL 让你的决赛桌
DECLARE @myTable TABLE
(Id INT,
Name VARCHAR(5000),
Title VARCHAR(5000)
);
DECLARE @lastId INT= 0, @id INT, @name VARCHAR(5000), @title VARCHAR(5000);
--for each record in table perform splitting and insertion in new table
WHILE EXISTS
(
SELECT 1
FROM StackOverFlowTable1 soft
WHERE Id > @lastId
)
BEGIN
SELECT TOP (1) @id = Id,
@name = Name,
@title = Title
FROM StackOverFlowTable1 soft
WHERE Id > @lastId
ORDER BY Id;
SET @lastId = @id;
INSERT INTO @myTable
(Id,
Name,
Title
)
SELECT @id,
ss1.Item,
ss2.Item
FROM dbo.SplitString(@name, ';') ss1
INNER JOIN dbo.SplitString(@title, ';') ss2 ON ss1.ItemNumber = ss2.ItemNumber
WHERE ss1.Item <> ''
AND ss2.Item <> '';
END;
SELECT * FROM @myTable;
【讨论】:
我不同意 Dinesh 脚本,因为它基于 RBAR。
我有非常相似的拆分函数,也返回 row_number 和 item。
所以测试我的脚本和其他示例数据。
DECLARE @Table TABLE(ID INT, NAME VARCHAR(10),TITLE VARCHAR(10))
INSERT INTO @Table VALUES (1,',a,b,c',',12,13,14')
SELECT id
,t.RowVal
,a.RowVal
FROM (
SELECT t.id
,a.RowNum
,a.RowVal
,t.TITLE
FROM @Table t
CROSS APPLY (
SELECT *
FROM dbo.FN_SPLIT_VALUE(t.NAME)
) a
) t
CROSS APPLY (
SELECT *
FROM dbo.FN_SPLIT_VALUE(t.TITLE)
WHERE t.RowNum = RowNum
) a
WHERE t.RowVal <> ''
【讨论】: