在SQL中编辑字符串列-删除分隔符之间的部分

时间:2019-01-29 16:18:37

标签: sql tsql sql-server-2012

我的表中有一个字符串列,其中包含“以字符分隔”的数据,例如:

  

“值|数据| 4 | Z | 2012年11月6日”

此数据被馈送到“解析器”中并反序列化为特定对象。 (此细节无关紧要,无法更改)

我的对象的结构已更改,现在我想摆脱数据的某些“部分”

所以我希望以前的值变成这个

  

“值|数据| 2012年11月6日”

我希望我能在T-SQL中获得一些帮助。

数据始终具有相同的节数,'n',而我将要为所有行'n-x和'n-y'删除相同的节

到目前为止,我知道我需要一条update语句来更新我的列值。 我发现了分割字符串的各种方法,但是我正在努力将其应用于我的场景。

在C#中我会做

string RemoveSecitons(string value)
{
    string[] bits = string.split(value,'|');

    List<string> wantedBits = new List<string>();


    for(var i = 0; i < bits.Length; i++)
    {
        if ( i==2 || i==3) // position of sections I no longer want
        {
            continue;
        }
        wantedBits.Add(bits[i]);
    }

    return string.Join(wantedBits,'|');

}

但是我如何在SQL中执行此操作,我不确定从哪里开始。这里的任何帮助将不胜感激

谢谢

Ps。我需要在SQL Server 2012上运行此SQL

编辑:看起来以某种方式解析为xml可能是一个流行的答案,但是我不能保证我的字符串不会包含诸如'<'或'&'

之类的字符。

5 个答案:

答案 0 :(得分:3)

使用NGrams8K,您可以轻松编写讨厌的快速自定义拆分器。这里的逻辑基于DelimitedSplit8K。即使您发布的C#代码也可能会胜过。

DECLARE @string VARCHAR(8000) = '"Value|Data|4|Z|11/06/2012"',
        @delim  CHAR(1)       = '|';

SELECT newString = 
(
  SELECT SUBSTRING(
           @string, split.pos+1,
           ISNULL(NULLIF(CHARINDEX(@delim,@string,split.pos+1),0),8000)-split.pos)
  FROM
  (
    SELECT ROW_NUMBER() OVER (ORDER BY d.Pos), d.Pos
    FROM
    (
      SELECT 0 UNION ALL
      SELECT ng.position 
      FROM   samd.ngrams8k(@string,1) AS ng
      WHERE  ng.token = @delim
    ) AS d(Pos)
  ) AS split(ItemNumber,Pos)
  WHERE split.ItemNumber IN (1,2,5)
  ORDER BY split.ItemNumber
  FOR XML PATH('')
);

返回:

newString
----------------------------
"Value|Data|11/06/2012"

答案 1 :(得分:2)

您可以尝试一些XQuery

DECLARE @s VARCHAR(100)='Value|Data|4|Z|11/06/2012';

SELECT CAST('<x>' + REPLACE(@s,'|','</x><x>') + '</x>' AS XML)
       .value('concat(/x[1],"|",/x[2],"|",/x[5])','nvarchar(max)');

简而言之:通过一些字符串替换将值转换为XML。然后,我们使用XQuery-concat将第一个,第二个和第五个元素再次绑定在一起。

此版本的效率较低,但是可以使用禁止的字符进行安全操作

SELECT CAST('<x>' + REPLACE((SELECT @s AS [*] FOR XML PATH('')),'|','</x><x>') + '</x>' AS XML)
       .value('concat(/x[1],"|",/x[2],"|",/x[5])','nvarchar(max)')

答案 2 :(得分:2)

只需添加一个非xml选项即可获得乐趣:

编辑和警告-万一有人尝试此解决方案而又看不到注释...

HABO正确地指出,如果其中任何列中有句点(“。”),则很容易破坏它。 PARSENAME取决于4部分的命名结构,如果超过该名称,则将返回NULL。如果任何值包含另一个管道(“ |”)或添加另一个分隔列,则该解决方案也会中断-我的答案中的子字符串专门存在,作为对4部分命名的依赖的变通方法。例如,如果您尝试在具有7个分隔列的变量上使用此解决方案,则需要对其进行重新处理或废弃,以使用此处的其他答案之一。

std::vector<char>

答案 3 :(得分:2)

不是最优雅的方法,但是可以起作用:

SELECT SUBSTRING(@str,1, CHARINDEX('|',@str,CHARINDEX('|',@str,1)+1)-1)
   + SUBSTRING(@str, CHARINDEX('|',@str,CHARINDEX('|',@str,CHARINDEX('|',@str,CHARINDEX('|',@str,1)+1)+1)+1), LEN(@str))


----------------------
Value|Data|11/06/2012

答案 4 :(得分:0)

这是一种快速的方法。

CREATE FUNCTION [dbo].StringSplitXML
(
    @String  VARCHAR(MAX), @Separator CHAR(1)
)
RETURNS @RESULT TABLE(id int identity(1,1),Value VARCHAR(MAX))
AS
BEGIN    
 DECLARE @XML XML
 SET @XML = CAST(
    ('<i>' + REPLACE(@String, @Separator, '</i><i>') + '</i>')
    AS XML)

 INSERT INTO @RESULT
 SELECT t.i.value('.', 'VARCHAR(MAX)') 
 FROM @XML.nodes('i') AS t(i)
 WHERE t.i.value('.', 'VARCHAR(MAX)') <> ''

 RETURN
END
GO
SELECT * FROM dbo.StringSplitXML( 'Value|Data|4|Z|11/06/2012','|')
WHERE id not in (3,4)

请注意,使用UDF会使速度变慢,因此仅当您要使用的数据集较小时才应考虑使用此解决方案。