从SQL Server中的字符串中删除子字符串的第二个外观

时间:2012-05-05 03:05:41

标签: sql-server tsql substring

我需要从主字符串中删除子字符串的第二个外观,如果两个子字符串彼此相邻。 e.g:

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

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

任何人都可以采用高效的方式来实现这一目标吗?

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

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

2 个答案:

答案 0 :(得分:2)

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

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

SET NOCOUNT ON;
DECLARE @UpperLimit INT;
SET @UpperLimit = 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('.', 'nvarchar(max)'), 1, 1, '')
    );
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 :(得分:0)

我决定去操纵字符串。我认为执行查询需要更长的时间,但是在... 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