我使用以下函数将字符串拆分为行。它比我之前使用的函数快得多,但是我需要以某种方式更快地浏览这些数据(它是一个ETL工作):
ALTER FUNCTION [dbo].[ArrayToTable]
(
@InputString VARCHAR(MAX) = ''
, @Delimitter VARCHAR(1) = ','
)
RETURNS @RESULT TABLE([Position] INT IDENTITY, [Value] VARCHAR(MAX))
AS
BEGIN
DECLARE @XML XML
SELECT @XML = CONVERT(XML, SQL_TEXT)
FROM (
SELECT '<root><item>'
+ REPLACE(@InputString, @Delimitter, '</item><item>')
+ '</item></root>' AS SQL_TEXT
) dt
INSERT INTO @RESULT([Value])
SELECT t.col.query('.').value('.', 'VARCHAR(1000)') AS [Value]
FROM @XML.nodes('root/item') t(col)
RETURN
END
有人能想到更好/更快的方法将分隔的字符串转换成行吗?我在查询中使用cross apply
来加入这些结果。
有人能想到一种更有效的方法来将分隔的字符串转换成行吗?
答案 0 :(得分:6)
这是我所拥有的最高效的功能:
CREATE FUNCTION [Resource].[udf_SplitByXml]
(@Data NVARCHAR(MAX), @Delimiter NVARCHAR(5))
RETURNS @Table TABLE
( Data NVARCHAR(MAX)
, SequentialOrder INT IDENTITY(1, 1))
AS
BEGIN
DECLARE @TextXml XML;
SELECT @TextXml = CAST('<d>' + REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@Data, '&', '&'), '<', '<'), '>', '>'), '"', '"'), '''', '''), @Delimiter, '</d><d>') + '</d>' AS XML);
INSERT INTO @Table (Data)
SELECT Data = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(RTRIM(LTRIM(T.split.value('.', 'nvarchar(max)'))), '&', '&'), '<', '<'), '>', '>'), '"', '"'), ''', '''')
FROM @TextXml.nodes('/d') T(Split)
RETURN
END
以下是可用于测试结果的示例调用:
SELECT * FROM Resource.udf_SplitByXml('yes, no, maybe, so', ',');
SELECT * FROM Resource.udf_SplitByXml('who|what|where|when|why|how|Uh, I don''t know!', '|');
SELECT * FROM Resource.udf_SplitByXml('Government, Education, Non-profit|Energy & Power|Yes|No', '|');
SELECT * FROM Resource.udf_SplitByXml('Energy & Power|Some<Thing>Wicked''This"Way Comes', '|');
另一个选择是尝试基于Adam Machanic代码的CLR解决方案,该代码是性能测试in this blog的赢家。
答案 1 :(得分:0)
这是我刚才写的另一个函数,令我惊讶的是,它比上面的XML方法快一点,尽管只是勉强。 比较两个函数来处理1,000行分隔的字符串不会产生任何性能差异。处理50K行分隔字符串,XML方法需要129秒才能生成435,217个提取行,相比之下,为我的简单字符串操作方法生成提取的435,217行需要122秒。
所以它并没有真正快得多,但我想如果你处理数十万行会有所不同。我的功能的主要好处是它易于阅读和理解,它不依赖于可能在SQL Server的未来版本中更改的XML功能,并且它应该可以轻松移植到任何语言。我真的希望找到一些非常快的东西,但我想这是我们现在能做的最好的事情。
CREATE FUNCTION dbo.ufn_util_Split
(
@RawText varchar(max),
@SplitCharacter varchar(2)
)
RETURNS @t_Results TABLE
(
RowIndex int IDENTITY(1,1),
RowValue varchar(max)
)
AS
BEGIN
DECLARE @vc_RowValue varchar(max) = ''
DECLARE @vc_Remainder varchar(max) = CASE WHEN RIGHT(@RawText,1) = @SplitCharacter THEN @RawText ELSE @RawText + @SplitCharacter END --the string must end in the split character in order for this to work
DECLARE @int_SplitPosition int = 0
DECLARE @int_LenSplitChar int = 0
SELECT @int_LenSplitChar = LEN(@SplitCharacter)
--determine the first segment to start with
SELECT @int_SplitPosition = CHARINDEX(@SplitCharacter,@vc_Remainder)
WHILE (@int_SplitPosition > 0)
BEGIN
SELECT @vc_RowValue = LEFT(@vc_Remainder,@int_SplitPosition-1)
INSERT INTO @t_Results (RowValue) VALUES (@vc_RowValue)
--now strip off the segment we just extracted and determine where the next segment ends, and continue
SET @vc_Remainder = SUBSTRING(@vc_Remainder,@int_SplitPosition+@int_LenSplitChar,LEN(@vc_Remainder))
SELECT @int_SplitPosition = CHARINDEX(@SplitCharacter,@vc_Remainder)
CONTINUE
END
RETURN
END
答案 2 :(得分:0)
嗨,试试这个 -
create procedure sp_getAllItems
@input varchar(100)
as
BEGIN
create table #tmpFruits (name varchar(10))
Declare @Qry Varchar(500)
Set @Qry = ''
Select @Qry = @Qry + ' Insert into #tmpFruits '
Select @Qry = @Qry + Replace( 'Select ''' +
Replace(Replace(Replace(Replace(@input,CHAR(9),''),'
',''),CHAR(10),''),CHAR(13),'') , ',',''' Union Select ''') + ''''
Exec (@Qry)
select * from Fruitstest where name in (select name from #tmpFruits)
drop table #tmpFruits
END
exec sp_getAllItems @input = 'cherry,banana'