【问题标题】:Dealing with splitting strings in SQL在 SQL 中处理拆分字符串
【发布时间】:2014-12-30 12:06:22
【问题描述】:

目前我正在开展一个项目,我们刚刚添加了在一个字段中存储多个值的功能。以前,它存储一个值,但现在它包含多个。下面是我所说的一个例子。

例如。一个人的名字被传入 (John Smith)。现在用户可以传入多个人的名字,用';'分隔(约翰·史密斯;约翰·多伊;简·坦迪)。

我的问题是,我们有一个下拉列表的数据源,该数据源当前来自该字段。下面是它的 SQL。

Select Distinct Content
From LIB_DocumentAttribute A
   inner join LIB_PublicDocument B on A.PublicDocumentId = B.PublicDocumentId 
Where AttributeId = (Select AttributeId From LIB_Attribute Where FieldName='Author')
   and B.Status = 'Approved'

这现在有点工作。内容是包含多个名称的字段。现在,当加载下拉列表时,它会拉回连接的名称字符串(上面较长的名称)。我想为数据源分解它。到目前为止,我唯一的想法是根据“;”拆分数据。但是,我需要将拆分出来的数据应用到返回其余数据的表中。下面是我已经到达但被卡住的地方。

CREATE TABLE #Authors
(
    Content varchar(MAX)
)
CREATE TABLE #Temp1
(
    Content varchar(MAX)
)
CREATE TABLE #Temp2
(
    Content varchar(MAX)
)
CREATE TABLE #Temp3
(
    Content varchar(MAX)
)

--Load Authors table to store all Authors
INSERT INTO #Authors
Select Distinct Content
    From LIB_DocumentAttribute A
        inner join LIB_PublicDocument B on A.PublicDocumentId = B.PublicDocumentId 
    Where AttributeId = (Select AttributeId From LIB_Attribute Where FieldName='Author')
        and B.Status = 'Approved'

--Take multiple Authors separated by '; ' and add to Temp1
INSERT INTO #Temp1
SELECT REPLACE(Content, '; ', ';') FROM #Authors WHERE Content LIKE '%; %'

--Remove multiple Authors separated by '; ' from Authors table
DELETE FROM #Authors
WHERE Content LIKE '%; %'

--Take multiple Authors separated by ';' and add to Temp2
INSERT INTO #Temp2
SELECT Content FROM #Authors WHERE Content LIKE '%;%'

--Remove multiple Authors separated by ';' from Authors table
DELETE FROM #Authors
WHERE Content LIKE '%;%'

--Somewhow split data and merge back together

DROP TABLE #Authors
DROP TABLE #Temp1
DROP TABLE #Temp2
DROP TABLE #Temp3

编辑:

所以最后,我想出了一个解决方案,它利用了 Kumar 建议的一些部分。我按照他的建议创建了一个用于拆分字符串的函数,并添加了一些个人更改以使其正常工作。请注意,这是在一个表返回函数中,该表名为@Authors,并且它有一个名为 Content 的列。

BEGIN
DECLARE @Temp TABLE
(
    Content varchar(MAX)
)

--Load Authors table to store all Authors
INSERT INTO @Authors
    Select Distinct Content
    From LIB_DocumentAttribute A
        inner join LIB_PublicDocument B on A.PublicDocumentId = B.PublicDocumentId 
    Where AttributeId = (Select AttributeId From LIB_Attribute Where FieldName='Author')

--Take multiple Authors separated by ', ' and add to Temp
INSERT INTO @Temp
        SELECT REPLACE(Content, ', ', ',')
        FROM @Authors;

    --Remove multiple Authors separated by ', ' from Authors table
    DELETE FROM @Authors
    WHERE Content LIKE '%,%';


    --Readd multiple Authors now separated into Authors table
    INSERT INTO @Authors
        SELECT s.Content
        FROM @Temp
            OUTER APPLY SplitString(Content,',') AS s
        WHERE s.Content <> (SELECT TOP 1 a.Content FROM @Authors a WHERE s.Content = a.Content)

    RETURN
END

【问题讨论】:

  • 此功能可能对您有所帮助:stackoverflow.com/questions/17543934/…
  • 仅供参考,如果您曾经想要在这个新字段中搜索单个值,最好创建一个每行存储一个值的链接表。如果您现在不这样做,那么当您的查询变得缓慢且难以创建时,您会希望稍后再这样做。话虽如此,您可以创建一个存储过程来循环字段并解析字符串(这正是它成为一个缓慢过程的原因,尤其是在批处理操作期间,当您处理一组中的多行时)。
  • @ps2goat 唯一没有这样做的原因是原始表格是为动态元素设计的。我们在表中存储了几个字段,因此将它们集中在一起以使事情变得更简单。我自己,我也会做类似的事情,但现在就是这样。
  • @KristyWelsh 这并没有完全帮助。不过还是谢谢。
  • 我建议您在创建下拉列表的应用程序代码中拆分名称。您还可以考虑将分隔符更改为保证不会出现在名称中的内容,例如 chr(30)。

标签: sql sql-server


【解决方案1】:

在提琴手链接http://sqlfiddle.com/#!3/390f8/11查看演示

Create table test(name varchar(1000));

    Insert into test values('AAA BBB; CCC DDD; eee fff');
    CREATE FUNCTION SplitString
    (    
          @Input NVARCHAR(MAX),
          @Character CHAR(1)
    )
    RETURNS @Output TABLE (
          Item NVARCHAR(1000)
    )
    AS
    BEGIN
          DECLARE @StartIndex INT, @EndIndex INT

          SET @StartIndex = 1
          IF SUBSTRING(@Input, LEN(@Input) - 1, LEN(@Input)) <> @Character
          BEGIN
                SET @Input = @Input + @Character
          END

          WHILE CHARINDEX(@Character, @Input) > 0
          BEGIN
                SET @EndIndex = CHARINDEX(@Character, @Input)

                INSERT INTO @Output(Item)
                SELECT SUBSTRING(@Input, @StartIndex, @EndIndex - 1)

                SET @Input = SUBSTRING(@Input, @EndIndex + 1, LEN(@Input))
          END

          RETURN
    END



Declare @name varchar(100)
Declare @table as table(name varchar(1000))
Declare cur cursor for
Select name from test

Open cur
fetch next from cur into @name
while (@@FETCH_STATUS = 0)
begin
Insert into @table
   Select * from dbo.splitstring(@name,';')
    fetch next from cur into @name
end
close cur
deallocate cur


Select * from @table

【讨论】:

  • 这绝对是朝着我需要走的方向迈出的一步。唯一的问题是我需要它为多行工作。如果可能的话,你能重做它来做到这一点吗?谢谢。
  • 感谢您的帮助。你给了我很好的方向,所以我给你信用。我的解决方案并不完全相同,但同样,你给了我足够多的想法来完成一个。再次感谢。
【解决方案2】:

这可能行得通。

drop table authors
GO
create table Authors (Author_ID int identity (1,1),name varchar (255), category varchar(255))
GO
insert into authors
(name,category)
select 
'jane doe','nonfiction'
union 
select
'Jules Verne; Mark Twain; O. Henry', 'fiction'
union 
select 
'John Smith; John Doe', 'nonfiction'
GO
DECLARE @table TABLE (
names VARCHAR(255)
,id INT
)
DECLARE @category VARCHAR(255)

SET @category = 'nonfiction'

DECLARE @Author_ID INT

DECLARE AuthorLookup CURSOR
FOR
SELECT Author_ID
FROM authors
WHERE category = @category

OPEN AuthorLookup

FETCH NEXT
FROM AuthorLookup
INTO @Author_ID

WHILE @@FETCH_STATUS = 0
BEGIN
IF (
        SELECT CHARINDEX(';', NAME, 0)
        FROM authors
        WHERE Author_ID = @Author_ID
        ) = 0
BEGIN
    INSERT INTO @table
    SELECT NAME
        ,Author_ID
    FROM authors
    WHERE Author_ID = @Author_ID
END
ELSE
BEGIN
    DECLARE @value VARCHAR(255)

    SELECT @value = NAME
    FROM authors
    WHERE Author_ID = @Author_ID

    WHILE len(@value) > 0
    BEGIN
        INSERT INTO @table
        SELECT substring(@value, 0, CHARINDEX(';', @value, 0))
            ,@Author_ID

        SELECT @value = replace(@value, substring(@value, 0, CHARINDEX(';', @value, 0) + 2), '')

        IF CHARINDEX(';', @value, 0) = 0
        BEGIN
            INSERT INTO @table
            SELECT @value
                ,@Author_ID

            SET @value = ''
        END
    END
END

FETCH NEXT
FROM AuthorLookup
INTO @Author_ID
END

CLOSE AuthorLookup

DEALLOCATE AuthorLookup

SELECT *
FROM @table

【讨论】:

    【解决方案3】:

    你可以创建函数来分割你的数据

    create function SplitString
    (    
       @data nvarchar(max),
       @sep char(1)
    )
    returns @result table (data nvarchar(max))
    as
    begin
      declare @i int
    
      while 1 = 1
      begin
        select @i = charindex(@sep, @data)
    
        if @i = 0
        begin
          insert into @result
          select @data
    
          break
        end
    
        insert into @result
        select rtrim(left(@data, @i - 1))
    
        select @data = ltrim(right(@data, len(@data) - @i))
      end
    
      return
    end
    

    并像这样使用它:

    select s.data
    from test as t
        outer apply SplitString(t.data,';') as s
    

    如果你确定你的数据中没有特殊字符,你也可以考虑使用 xml:

    ;with cte as (
        select
            cast('<s>' + replace(data, ';', '</s><s>') + '</s>' as xml) as data
        from test
    )
    select
        t.c.value('.', 'nvarchar(max)') as data
    from cte
        outer apply data.nodes('s') as t(c)
    

    sql fiddle demo

    【讨论】:

      猜你喜欢
      • 2021-08-26
      • 1970-01-01
      • 1970-01-01
      • 2021-01-06
      • 2014-02-21
      • 1970-01-01
      • 1970-01-01
      • 2021-12-15
      • 1970-01-01
      相关资源
      最近更新 更多