我正在使用SQL Server数据库。我有一个包含分隔列表的列,我需要编写一个查询,将列表的值拆分为行。从浏览StackOverflow和网络的其余部分,我知道这是一个常见的问题。事实上,我在这里找到了一个广泛的分析:
http://www.sommarskog.se/arrays-in-sql.html
不幸的是,我在该网站和其他地方看到的每个解决方案都要求我创建一个功能。这不是我的选择 - 我缺乏使用CREATE命令所需的权限。
没有CREATE,我知道我可以使用PARSENAME函数,就像这样(感谢Nathan Bedford在How do I split a string so I can access item x?。):
SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 2)
但是,PARSENAME仅适用于4个或更少项目的列表。因此,我的问题是:如何在不在数据库中创建新对象的情况下编写查询来拆分超过4个项目的分隔字符串?
编辑:
感谢大家的快速解答。我可能遗漏了一些重要信息 - 我通过ODBC连接与数据库进行交互。除了CREATE语句之外,似乎还有其他语句不起作用。例如,我似乎无法在一个语句中使用DECLARE来定义将在另一个语句中使用的变量。尽可能接近我的想法,我必须将所有内容都放在一个SELECT语句中(尽管WITH似乎也适用于声明公共表)。不幸的是,到目前为止所提出的所有解决方案似乎都需要在SELECT语句之外进行变量声明,而这是不起作用的。请忍受我 - 我正在学习。
答案 0 :(得分:15)
使用XML的版本。
declare @S varchar(100) = 'Hello John Smith'
select
n.r.value('.', 'varchar(50)')
from (select cast('<r>'+replace(@S, ' ', '</r><r>')+'</r>' as xml)) as s(XMLCol)
cross apply s.XMLCol.nodes('r') as n(r)
使用表格代替
将@T
替换为您正在使用的表格。
-- Test table
declare @T table (ID int, Col varchar(100))
insert into @T values (1, 'Hello John Smith')
insert into @T values (2, 'xxx yyy zzz')
select
T.ID,
n.r.value('.', 'varchar(50)')
from @T as T
cross apply (select cast('<r>'+replace(replace(Col,'&','&'), ' ', '</r><r>')+'</r>' as xml)) as S(XMLCol)
cross apply S.XMLCol.nodes('r') as n(r)
在不使用变量
的情况下拆分字符串'Hello John Smith'
select
n.r.value('.', 'varchar(50)')
from (select cast('<r>'+replace('Hello John Smith', ' ', '</r><r>')+'</r>' as xml)) as s(XMLCol)
cross apply s.XMLCol.nodes('r') as n(r)
答案 1 :(得分:7)
使用内置master..spt_values表的示例
DECLARE @String VARCHAR(1000)
SELECT @String ='1,4,77,88,4546,234,2,3,54,87,9,6,4,36,6,9,9,6,4,4,68,9,0,5'
SELECT SUBSTRING(',' + @String + ',', Number + 1,
CHARINDEX(',', ',' + @String + ',', Number + 1) - Number -1)AS VALUE
FROM master..spt_values
WHERE Type = 'P'
AND Number <= LEN(',' + @String + ',') - 1
AND SUBSTRING(',' + @String + ',', Number, 1) = ','
GO
请点击此处了解详情:Split A String By Using A Number Table
答案 2 :(得分:3)
您可以使用递归CTE逐步提取一个项目
样本表
create table aTable(a int identity primary key, b int, c varchar(100))
insert aTable values (1, 'this is a test string')
insert aTable values (1, 'this is another test string')
insert aTable values (2, 'here is a test string to put the others to shame')
insert aTable values (4, '')
insert aTable values (5, null)
insert aTable values (5, '-the end- ')
查询
;with tmp(a, b, c, position, single) as (
select a, b,
STUFF(c, 1, CHARINDEX(' ', c + ' .'), ''),
1,
convert(nvarchar(max),left(c, CHARINDEX(' ', c + ' .') -1))
from aTable
union all
select a, b,
STUFF(c, 1, CHARINDEX(' ', c + ' .'), ''),
position+1,
convert(nvarchar(max),left(c, CHARINDEX(' ', c + ' .') -1))
from tmp
where c > '')
select a, b, single, position
from tmp
order by a, position
注意:
' .'
中的点是必需的,因为SQL Server通常不会将尾随空格视为重要空格。a
和b
的示例,其中列c
被拆分为single
,另外一列用于指示位置。< / LI>
答案 3 :(得分:3)
对于这个问题的后来者,http://www.sqlperformance.com/2012/07/t-sql-queries/split-strings上的文章为各种选项提供了出色的性能分析。考虑的一些选项包括(从网站复制以供参考):
<强> CLR 强>
。http://dataeducation.com/sqlclr-string-splitting-part-2-even-faster-even-more-scalable/处的.Net代码。
CREATE ASSEMBLY CLRUtilities FROM 'c:\DLLs\CLRUtilities.dll'
WITH PERMISSION_SET = SAFE;
GO
CREATE FUNCTION dbo.SplitStrings_CLR
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255)
)
RETURNS TABLE ( Item NVARCHAR(4000) )
EXTERNAL NAME CLRUtilities.UserDefinedFunctions.SplitString_Multi;
GO
<强> XML 强>
CREATE FUNCTION dbo.SplitStrings_XML
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
SELECT Item = y.i.value('(./text())[1]', 'nvarchar(4000)')
FROM
(
SELECT x = CONVERT(XML, '<i>'
+ REPLACE(@List, @Delimiter, '</i><i>')
+ '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i)
);
GO
<强> CTE 强>
CREATE FUNCTION dbo.SplitStrings_CTE
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255)
)
RETURNS @Items TABLE (Item NVARCHAR(4000))
WITH SCHEMABINDING
AS
BEGIN
DECLARE @ll INT = LEN(@List) + 1, @ld INT = LEN(@Delimiter);
WITH a AS
(
SELECT
[start] = 1,
[end] = COALESCE(NULLIF(CHARINDEX(@Delimiter,
@List, @ld), 0), @ll),
[value] = SUBSTRING(@List, 1,
COALESCE(NULLIF(CHARINDEX(@Delimiter,
@List, @ld), 0), @ll) - 1)
UNION ALL
SELECT
[start] = CONVERT(INT, [end]) + @ld,
[end] = COALESCE(NULLIF(CHARINDEX(@Delimiter,
@List, [end] + @ld), 0), @ll),
[value] = SUBSTRING(@List, [end] + @ld,
COALESCE(NULLIF(CHARINDEX(@Delimiter,
@List, [end] + @ld), 0), @ll)-[end]-@ld)
FROM a
WHERE [end] < @ll
)
INSERT @Items SELECT [value]
FROM a
WHERE LEN([value]) > 0
OPTION (MAXRECURSION 0);
RETURN;
END
GO
<强>功能强>
CREATE FUNCTION dbo.SplitStrings_Moden
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING AS
RETURN
WITH E1(N) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1),
E2(N) AS (SELECT 1 FROM E1 a, E1 b),
E4(N) AS (SELECT 1 FROM E2 a, E2 b),
E42(N) AS (SELECT 1 FROM E4 a, E2 b),
cteTally(N) AS (SELECT 0 UNION ALL SELECT TOP (DATALENGTH(ISNULL(@List,1)))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E42),
cteStart(N1) AS (SELECT t.N+1 FROM cteTally t
WHERE (SUBSTRING(@List,t.N,1) = @Delimiter OR t.N = 0))
SELECT Item = SUBSTRING(@List, s.N1, ISNULL(NULLIF(CHARINDEX(@Delimiter,@List,s.N1),0)-s.N1,8000))
FROM cteStart s;
事实证明,最佳性能来自使用CLR功能,而XML解决方案也表现良好。几乎在所有情况下,使用数字表(上面没有重复的方法)会导致最差的表现。
答案 4 :(得分:2)
对于sql Server&gt; = 2016,您可以使用string_split,如下所示:
SELECT * FROM string_split('Hello John Smith', ' ')
输出
+-------+
| value |
+-------+
| Hello |
| John |
| Smith |
+-------+
答案 5 :(得分:0)
你看过数字表吗?
http://www.sqlservercentral.com/articles/T-SQL/62867/
http://bradsruminations.blogspot.com/2010/08/integer-list-splitting-sql-fable.html
答案 6 :(得分:0)
我只需要创建一个表的许多函数中的一个,而不是让它返回将值放在表变量中。然后使用表变量。这是一个返回表的示例。
http://www.codeproject.com/KB/database/SQL_UDF_to_Parse_a_String.aspx
答案 7 :(得分:0)
使用UDF最有意义的是它对所有项目都很灵活,我通常使用的是我的博客,
http://sqlthis.blogspot.com/2005/02/list-to-table.html
因为它是为了获取输入字符串和分隔符而编写的,所以它可以是分隔符的任何单个字符。我在上面的链接中写了一个,但之后我发现许多网站发布了类似的解决方案,所以部分可能是受到我所属论坛的启发...
它有效,希望它也适合你。
按照建议编辑 2017-08-09,我已经克隆了下面的代码块。谢谢!
CREATE FUNCTION udfListToTable (@HList VarChar(2000), @Delimiter CHAR(1))
RETURNS @ListTable TABLE (Field1 VARCHAR(6))
AS
BEGIN
--By: Francisco Tapia
--Date: 2/1/2005
--Purpose: To convert a Comma delimited text to a Temp Variable table To help avoid dynamic sql
-- Instead you can join the temp table or use it in your where clause if a field is IN the subquery
DECLARE @FieldText as VarChar(6)
IF RIGHT(RTRIM(@HLIST),1) <>@Delimiter
SET @HList = @HList + @Delimiter
WHILE CHARINDEX(@Delimiter, @HList) > 0
BEGIN
IF CHARINDEX(@Delimiter, @HList) > 0
BEGIN
SELECT @FieldText =LEFT(@HList, CHARINDEX(@Delimiter, @HList)-1)
END
ELSE
BEGIN
SELECT @FieldText = RTRIM(LTRIM(@HList))
END
--Insert into Variable Table
INSERT INTO @ListTable(Field1)
SELECT RTRIM(LTRIM(@FieldText))
--Remove Item from list
SELECT @HList = RIGHT(RTRIM(@HList), LEN(RTRIM(@HList)) -
CHARINDEX(@Delimiter, @HList))
END
RETURN
END
答案 8 :(得分:0)
DECLARE @cols AS NVARCHAR(max),
@Val VARCHAR(100)='Hi- Hello break this-Wall',
@Deli VARCHAR(50)='-',
@query AS NVARCHAR(max)
SELECT @cols = Stuff((SELECT ',' + Quotename(id)
FROM (SELECT stringpieceid AS ID,
stringpiece
FROM
[Utility].[dbo].[Splitstringtotable](@Val, @Deli))
X
FOR xml path(''), type).value('.', 'NVARCHAR(MAX)'), 1, 1,
'')
SELECT @query =
'SELECT * FROM (SELECT StringPieceID as ID,StringPiece from [Utility].[dbo].[SplitStringToTable]('''
+ @Val + ''',''' + @Deli + '''))X PIVOT ( MAX(StringPiece) for [ID] in (' + @cols
+ ') ) P'
PRINT @query
EXEC Sp_executesql
@query
答案 9 :(得分:0)
{{1}}
- 让我知道你是否有理解这个代码
答案 10 :(得分:0)
这是一个用户定义的解析功能,该功能使SQL Server的性能也类似于VB的“拆分”功能。专为交互式杠杆而设计;例如,解析从外部API调用的存储过程中的数据。
https://gallery.technet.microsoft.com/scriptcenter/User-def-function-enabling-98561cce