【问题标题】:CTE/recursive query to split delimited strings only returning one row?CTE/递归查询拆分分隔字符串只返回一行?
【发布时间】:2015-09-24 21:37:23
【问题描述】:

我已应用“借用”解决方案将分隔字符串拆分为行(我正在使用 MSSQL 2008 R2),但该解决方案特定于 CTE/递归查询。这很好用,但只转换一行数据。我将如何调整它以返回表中的所有行(或者,更好的是,能够包含 where 子句)?

我已经为此研究了两天,而且由于我对 CTE/递归查询或 XML 没有太多经验……欢迎任何专业知识!谢谢!!

DECLARE @RowData varchar(2000)
DECLARE @SplitOn varchar(1)
DECLARE @ObjectID int

SELECT 
@ObjectID = ObjectID, @RowData = ObjectName, @SplitOn = ';' from Objects 

declare @xml as xml
SET @XML  = '<t><r>' +  Replace(@RowData , @spliton, '</r><r>') + '</r></t>'  

select @objectid as objectid, rtrim(ltrim(t.r.value('.', 'VARCHAR(8000)'))) as splitvalue
from @xml.nodes('/t/r') as t(r) 

【问题讨论】:

  • 表格中的数据是什么样的?

标签: sql-server xml sql-server-2008 recursion


【解决方案1】:

我将首先创建一个表值函数,该函数像此处找到的那样进行字符串拆分:http://ole.michelsen.dk/blog/split-string-to-table-using-transact-sql.html

然后使用 OUTER APPLY 将这些用于表中的行。这是一个查询,因此您可以应用 where 子句。这是一个创建函数的示例,一个我用一些测试数据填充的临时表和选择语句。

CREATE FUNCTION [dbo].[Split]
(
    @String NVARCHAR(4000),
    @Delimiter NCHAR(1)
)
RETURNS TABLE
AS
RETURN
(
    WITH Split(stpos,endpos)
    AS(
        SELECT 0 AS stpos, CHARINDEX(@Delimiter,@String) AS endpos
        UNION ALL
        SELECT endpos+1, CHARINDEX(@Delimiter,@String,endpos+1)
            FROM Split
            WHERE endpos > 0
    )
    SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
        'Data' = SUBSTRING(@String,stpos,COALESCE(NULLIF(endpos,0),LEN(@String)+1)-stpos)
    FROM Split
)
GO

IF ( OBJECT_ID(N'tmpdb..#Object') IS NOT NULL ) DROP TABLE [#Object];

CREATE TABLE [#Object]
(
  [ObjectId] INT NOT NULL IDENTITY(1, 1)
, [Object] VARCHAR(1000) NOT NULL
);

INSERT INTO #Object
        ( [Object] )
VALUES  ( 'brad;bill;jerry'), ('Scott;MATT;DEAN'), ('larry;bob;john')

GO

SELECT  [tt].[ObjectId]
      , [s].[Data]
FROM    #Object AS [tt]
        OUTER APPLY dbo.Split([tt].[Object], ';') AS s
WHERE   tt.[ObjectId] < 3

只需将临时表的内容替换为您的表即可。希望对您有所帮助!

【讨论】:

    【解决方案2】:

    您没有得到超过一行的原因是因为您正在以编程方式而不是以集合为中心的方式来考虑集合。

    SELECT @ObjectID .... 获取一行...最后一行。

    我建议你使用 CROSS APPLY:

    -- Data Setup
    CREATE TABLE #Objects ( ObjectID INTEGER, ObjectName VARCHAR(2000) )
    INSERT INTO #Objects ( ObjectID, ObjectName )
    SELECT 1, 'foo;bar' UNION
    SELECT 2, 'biz;baz;buz'
    
    -- The Meat and Potatoes. You can add a WHERE to your inner or outer query
    SELECT 
      ObjectID,
      RTRIM( LTRIM( t.r.value( '.', 'VARCHAR(8000)' ) ) ) AS splitvalue
    FROM
      (
        SELECT
          ObjectID,
          CONVERT( XML, '<t><r>' + REPLACE( ObjectName, ';', '</r><r>' ) + '</r></t>' ) AS xml_part
        FROM
          #Objects
      ) AS xml_part_builder
      CROSS APPLY xml_part_builder.xml_part.nodes('/t/r') AS t(r)
    
    -- CLEAN UP
    DROP TABLE #Objects
    

    如果这有帮助,请告诉我。

    干杯!

    【讨论】:

    • 非常感谢您的解决方案和解释。我遇到了 XML 问题,因为我的字符串有空格,但除此之外,您的解决方案效果很好。 @DB_Brad,我最终选择了您的解决方案(将 TVF 调用到查询中),我很欣赏您建议的功能的简单性。就像我上面提到的那样,我遇到了 XML 的问题,所以您的解决方案非常适合。谢谢!!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多