首页 > 解决方案 > 如何在两个相同的分隔符 T-SQL 之间提取字符串?

问题描述

我想从具有多个相同分隔符的值中提取字符串的一部分。

这是我正在使用的数据的示例(这些文件路径可能会更长,具体取决于文件的深度):

文件路径:

问:\12345\downloads\randomfilename.png

问:\123_4566\downloads\randomfilename.pdf

问:\CCCMUD\downloads\randomfilename.mp4

我想将每一行的前两个定界符( \ )之间的部分字符串提取到一个新列中,例如

12345

123_4566

CCCMUD

我知道我需要使用 SUBSTRING 和 CHARINDEX 但我不确定如何。我将不胜感激任何帮助。谢谢。

标签: sqlsql-serverstringtsql

解决方案


一种简单有效的方法是使用序数拆分器(例如这个)。为确保拆分值仅包含您可以添加的数字WHERE try_cast(ds.Item as int) is not null。像这样的东西

分离器

CREATE FUNCTION [dbo].[DelimitedSplit8K_LEAD]
--===== Define I/O parameters
        (@pString VARCHAR(8000), @pDelimiter CHAR(1))
RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
  WITH E1(N) AS (
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
                ),                          --10E+1 or 10 rows
       E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
       E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
 cteTally(N) AS (--==== This provides the "zero base" and limits the number of rows right up front
                     -- for both a performance gain and prevention of accidental "overruns"
                 SELECT 0 UNION ALL
                 SELECT TOP (DATALENGTH(ISNULL(@pString,1))) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
                ),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
                 SELECT t.N+1
                   FROM cteTally t
                  WHERE (SUBSTRING(@pString,t.N,1) = @pDelimiter OR t.N = 0) 
                )
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
 SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY s.N1),
        Item = SUBSTRING(@pString,s.N1,ISNULL(NULLIF((LEAD(s.N1,1,1) OVER (ORDER BY s.N1) - 1),0)-s.N1,8000))
   FROM cteStart s
;

询问

select ds.*
from @s s
     cross apply dbo.DelimitedSplit8K_LEAD(s.[path], '\') ds
where ds.ItemNumber=2
      and try_cast(ds.Item as int) is not null;
ItemNumber  Item
2           12345

推荐阅读