【问题标题】:Remove second appearence of a substring from string in SQL Server从 SQL Server 中的字符串中删除子字符串的第二次出现
【发布时间】:2012-05-05 03:05:41
【问题描述】:

如果两个子字符串彼此相邻,我需要从主字符串中删除第二次出现的子字符串。例如:

Jhon\Jhon\Jane\Mary\Bob 需要结束 Jhon\Jane\Mary\Bob

Mary\Jane\Mary\Bob 必须保持不变。

任何人都可以提出一种高效的方式来做到这一点吗?

'\'是不同名称的分隔符,可以作为要替换的子串的限制。

编辑:这是在 SELECT 语句上运行的,所以它应该是一个单行解决方案,我不能使用变量。 另外,如果名字在其他地方重复,我必须让他们在那里。如果第一个和第二个名称相同,则仅删除一个匹配项。

【问题讨论】:

  • 您希望 SQL Server 执行此操作,还是希望它更快?缺少 CLR,我认为两者都做不到。您可以使用拆分 UDF 执行此操作,但在大型结果集上不会很快。我认为您最好在语言更好地支持它的客户端执行字符串操作,并且无论如何您都会在这里循环......
  • @AaronBertrand 我想要这一切! SP 的结果是发送给客户端的报告,它只是将其转换为 csv 文件,所以我不能这样做......另一方面,报告可以有 200K 行,我不知道是不是太多了……
  • @AlejoBrz 有很多优秀的 ETL 工具可以做到这一点。 SSIS 就是其中之一,它带有 SQL 服务器。将报表用作 ETL 工具并非可行之道。
  • 当然,将报告输出写入 CSV 文件的任何内容也可以解析每一行并在将每一行写入文件时对其进行修改。还有你想用Jhon\Jhon\Jhon\Jane\Mary\Bob做什么?
  • @AaronBertrand 我只需要删除第一个重复的名称。它不应该出现第三次,但如果出现,我必须把它留在那里。

标签: sql-server tsql substring


【解决方案1】:

所以这是一个尝试,但正如我所说,我认为您不会在本机 T-SQL 中获得 快速 解决方案。

首先,如果您还没有数字表,请创建一个:

SET NOCOUNT ON;
DECLARE @UpperLimit int = 4000;

;WITH n AS
(
    SELECT rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
    FROM sys.all_objects AS s1
    CROSS JOIN sys.all_objects AS s2
)
SELECT [Number] = rn - 1
INTO dbo.Numbers FROM n
WHERE rn <= @UpperLimit + 1;

CREATE UNIQUE CLUSTERED INDEX n ON dbo.Numbers([Number]);

然后创建两个函数。一个将字符串拆分成一个表,然后另一个重新加入第一个函数的结果但忽略任何后续重复项。

CREATE FUNCTION dbo.SplitStrings
(
    @List  nvarchar(4000),
    @Delim char(1)
)
RETURNS TABLE
AS
    RETURN ( SELECT 
      rn = ROW_NUMBER() OVER (ORDER BY CHARINDEX(@Delim, @List + @Delim)),
      [Value] = LTRIM(RTRIM(SUBSTRING(@List, [Number],
      CHARINDEX(@Delim, @List + @Delim, [Number]) - [Number])))
      FROM dbo.Numbers
      WHERE Number <= LEN(@List)
      AND SUBSTRING(@Delim + @List, [Number], 1) = @Delim
    );
GO

第二个功能:

CREATE FUNCTION dbo.RebuildString
(
    @List  nvarchar(4000),
    @Delim char(1)
)
RETURNS nvarchar(4000)
AS
BEGIN
    RETURN ( SELECT newval = STUFF((
     SELECT @Delim + x.[Value] FROM dbo.SplitStrings(@List, @Delim) AS x
      LEFT OUTER JOIN dbo.SplitStrings(@List, @Delim) AS x2
      ON x.rn = x2.rn + 1
      WHERE (x2.rn IS NULL OR x.value <> x2.value)
      ORDER BY x.rn
      FOR XML PATH(''), TYPE).value(N'./text()[1]', N'nvarchar(max)'), 1, 1, N'')
    );
END
GO

现在您可以针对您在问题中提供的两个示例进行尝试:

;WITH cte(colname) AS
(
    SELECT 'Jhon\Jhon\Jane\Mary\Bob'
    UNION ALL SELECT 'Mary\Jane\Mary\Bob'
)
SELECT dbo.RebuildString(colname, '\')
FROM cte;

结果:

Jhon\Jane\Mary\Bob
Mary\Jane\Mary\Bob

但我强烈、强烈、强烈建议您在决定使用它之前根据您的典型数据大小彻底测试它。

【讨论】:

  • +1 以获得如此详尽的回应!为了便于维护,我决定使用字符串操作,我尝试使用它的报告,但事实证明它不会增加响应时间。
【解决方案2】:

我决定进行字符串操作。我认为执行查询需要更长的时间,但是在... ejem ...生产环境中进行测试... ejem ...我发现它没有(令我惊讶的是)。它不漂亮,我知道,但它很容易维护......

这是我最终查询的简化版本:

SELECT SOQ.PracticeId,
       CASE WHEN LEFT(SOQ.myString, SOQ.SlashPos) = SUBSTRING(SOQ.myString, SOQ.SlashPos + 1, LEN(LEFT(SOQ.myString, SOQ.SlashPos)))
            THEN RIGHT(SOQ.myString, LEN(SOQ.myString) - SOQ.SlashPos)
            ELSE SOQ.myString
       END as myString
  FROM (SELECT OQ.AllFields, OQ.myString, CHARINDEX('\', OQ.myString, 0) as SlashPos
          FROM MyOriginalQuery OQ) SOQ

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-07
    • 2013-12-15
    • 2020-02-26
    • 1970-01-01
    • 2018-02-05
    • 2019-04-02
    • 2016-12-19
    • 1970-01-01
    相关资源
    最近更新 更多