【问题标题】:Efficient way to string split using CTE使用 CTE 进行字符串拆分的有效方法
【发布时间】:2011-06-17 05:10:27
【问题描述】:

我有一张看起来像的桌子

ID  Layout
1   hello,world,welcome,to,tsql
2   welcome,to,stackoverflow

期望的输出应该是

Id  Splitdata
1   hello
1   world
1   welcome
1   to
1   tsql
2   welcome
2   to
2   stackoverflow

我已通过以下查询完成此操作

Declare @t TABLE(
    ID  INT IDENTITY PRIMARY KEY,
    Layout VARCHAR(MAX)
)
INSERT INTO @t(Layout)
SELECT 'hello,world,welcome,to,tsql' union all
SELECT 'welcome,to,stackoverflow'
--SELECT * FROM @t
;With cte AS(
select F1.id
 ,O.splitdata 
 from
 (
 select *,
 cast('<X>'+replace(F.Layout,',','</X><X>')+'</X>' as XML) as xmlfilter
 from @t F
 )F1
 cross apply
 ( 
 select fdata.D.value('.','varchar(MAX)') as splitdata 
 from f1.xmlfilter.nodes('X') as fdata(D)) O
 )

 select * from cte

但是从性能上来说它是非常糟糕的。我正在寻找更有效的查询,但仅使用 CTE。

【问题讨论】:

  • 尝试 [this post](wisesoft.co.uk/scripts/t-sql_cte_split_string_function.aspx) 中的 proc,他们使用递归进行拆分。我认为你可以适应它。这使用 CTE。
  • @Joel 虽然我同意你的观点,但有时有充分的理由知道如何有效地做到这一点。专门清理来自集成系统的不良数据,您必须定期将例程作为作业运行。我认为答案将对其他人具有启发性和帮助。
  • @NullRef - 同意,我的评论有些夸张。这就是为什么它是评论,而不是答案。但我也认为将通知放在适当的位置并为粗心的人投票也很重要。
  • @Joel 我对此无可争辩。我也无法与 CSV=666 争论!
  • 使用 CTE 如何使 T-SQL 更擅长字符串解析?

标签: sql sql-server sql-server-2005 tsql common-table-expression


【解决方案1】:

您似乎对使用 CTE 很执着,所以试试这个:

DECLARE @YourTable table (RowID int, Layout varchar(200))
INSERT @YourTable VALUES (1,'hello,world,welcome,to,tsql')
INSERT @YourTable VALUES (2,'welcome,to,stackoverflow')

;WITH SplitSting AS
(
    SELECT
        RowID,LEFT(Layout,CHARINDEX(',',Layout)-1) AS Part
            ,RIGHT(Layout,LEN(Layout)-CHARINDEX(',',Layout)) AS Remainder
        FROM @YourTable
        WHERE Layout IS NOT NULL AND CHARINDEX(',',Layout)>0
    UNION ALL
    SELECT
        RowID,LEFT(Remainder,CHARINDEX(',',Remainder)-1)
            ,RIGHT(Remainder,LEN(Remainder)-CHARINDEX(',',Remainder))
        FROM SplitSting
        WHERE Remainder IS NOT NULL AND CHARINDEX(',',Remainder)>0
    UNION ALL
    SELECT
        RowID,Remainder,null
        FROM SplitSting
        WHERE Remainder IS NOT NULL AND CHARINDEX(',',Remainder)=0
)
SELECT * FROM SplitSting ORDER BY RowID

输出:

RowID       Part                   
----------- -----------------------
1           hello                  
1           world                  
1           welcome                
1           to                     
1           tsql                   
2           welcome                
2           to                     
2           stackoverflow          

(8 row(s) affected)

这是一篇关于在 SQL Server 中拆分字符串的优秀文章:"Arrays and Lists in SQL Server 2005 and Beyond, When Table Value Parameters Do Not Cut it" by Erland Sommarskog

编辑这是另一个版本(但您需要一个数字表)返回与上述相同的结果:

;WITH SplitValues AS
(
    SELECT
        RowID,ListValue
        FROM (SELECT
                  RowID, LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(',', List2, number+1)-number - 1))) AS ListValue
                  FROM (
                           SELECT RowID, ',' + Layout + ',' AS List2
                           FROM @YourTable
                       ) AS dt
                      INNER JOIN Numbers n ON n.Number < LEN(dt.List2)
                  WHERE SUBSTRING(List2, number, 1) = ','
             ) dt2
        WHERE ListValue IS NOT NULL AND ListValue!=''
)
SELECT * FROM SplitValues

请参阅此处查看数字表:What is the best way to create and populate a numbers table?

【讨论】:

  • 它不考虑只有一项的条目,例如'hello'。
【解决方案2】:

来自 NullRef 的回答

根据我对sql server的理解,没有set操作的函数会更快

这样会更有效率

CREATE FUNCTION fnSplitString(@str nvarchar(max),@sep nvarchar(max))
RETURNS TABLE
AS
RETURN
    WITH a AS(
        SELECT CAST(0 AS BIGINT) as idx1,CHARINDEX(@sep,@str) idx2
        UNION ALL
        SELECT idx2+1,CHARINDEX(@sep,@str,idx2+1)
        FROM a
        WHERE idx2>0
    )
    SELECT SUBSTRING(@str,idx1,COALESCE(NULLIF(idx2,0),LEN(@str)+1)-idx1) as value
    FROM a

【讨论】:

    【解决方案3】:

    这是我使用 CTE 的最佳解决方案:

    DECLARE @Char VARCHAR(MAX) = '10||3112||||aaaa||'
    DECLARE @Separador CHAR(2) = '||'
    
    ;WITH Entrada AS(
        SELECT
            CAST(1 AS Int) As Inicio,
            CHARINDEX(@Separador, @Char) As Fim
        UNION ALL
        SELECT
            CAST(Fim + LEN(@Separador) AS Int) As Inicio,
            CHARINDEX(@Separador, @Char, Fim + 1) As Fim
        FROM Entrada
        WHERE CHARINDEX(@Separador, @Char, Fim + 1) > 0
    )
    SELECT 
        SUBSTRING(@Char, Inicio, Fim - Inicio)
    FROM Entrada
    WHERE (Fim - Inicio) > 0
    

    【讨论】:

      猜你喜欢
      • 2011-02-22
      • 1970-01-01
      • 1970-01-01
      • 2015-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多