【问题标题】:Select string between "\" and "." in SQL Server在“\”和“.”之间选择字符串在 SQL Server 中
【发布时间】:2019-05-12 02:29:06
【问题描述】:

我有一个名为 file 的表格列,它包含目录。 样本数据为:

C:\filedata\6860_f11.xlxb_3.30 test - 0.3 ML 

C:\cloud\files\1191_f12.xlxb_12.16 test - 0.3 ML 

请注意,我只想为#1 获取6860_f11.xlxb,为#2 获取1191_f12.xlxb

对于#1,该目录仅包含 1 个文件夹 filedata,但对于 #2,它包含 2 个文件夹 cloud\files

下面是我的代码:

select  
    (SUBSTRING((file), 0, CHARINDEX ('.xlxb', (file)) + 4)) as xlsb_file 
from
    [Projects].[dbo].[ProjFiles]

有什么方法可以让我在文件夹之后获取​​字符串,直到.xlxb 之后的下划线?

【问题讨论】:

  • 如果你反转字符串,那么你总是会找到第一个/,然后使用子字符串,然后再反转它。

标签: sql-server tsql substring


【解决方案1】:

不需要 CLR。不需要正则表达式。使用NGrams8K 解决此问题的最简单且性能最佳的方法。我住的地方现在是凌晨 2 点,所以我会尽快完成。

注意这个查询:

DECLARE @string VARCHAR(150) = 'C:\cloud\files\1191_f12.xlxb_12.16 test - 0.3 ML';

SELECT RetPos = f.p, RetVal = e.s
FROM   (SELECT MAX(position)+1 FROM samd.NGrams8k(@string,1) WHERE token = '\') AS f(p)
CROSS APPLY  (VALUES(SUBSTRING(@string,f.p,CHARINDEX('.',@string,f.p)-f.p+5)))  AS e(s);

结果:

RetPos RetVal
------ ---------------
16     1191_f12.xlxb

现在对着桌子:

CREATE TABLE #yourtable ([file] VARCHAR(150));
INSERT INTO #yourtable
VALUES ('C:\filedata\6860_f11.xlxb_3.30 test - 0.3 ML'),
       ('C:\cloud\files\1191_f12.xlxb_12.16 test - 0.3 ML');

SELECT *
FROM   #yourtable AS t
CROSS APPLY
(
  SELECT newstring = e.s
  FROM   (SELECT MAX(position) FROM samd.NGrams8k(t.[file],1) WHERE token = '\') AS f(p)
  CROSS 
  APPLY  (VALUES(SUBSTRING(t.[file],f.p+1,CHARINDEX('.',t.[file],f.p+1)-f.p+4)))  AS e(s)
) AS itvf_str_extract;

真的就这么简单。性能也将胜过任何基于 CLR/Regex 的解决方案——就是这样。

附带说明:John Cappelletti 的解决方案非常出色(与往常一样)。在引擎盖下,它与我的 NGrams 解决方案非常相似,但不完全一样;比较这两个查询:

DECLARE @string VARCHAR(150) = 'C:\cloud\files\1191_f12.xlxb_12.16 test - 0.3 ML';
DECLARE @Delimiter1 varchar(100) = '\', @Delimiter2 varchar(100) = '.';

-- Alan B
SELECT 
  RetSeq = 1,
  RetPos = f.p, 
  RetVal = e.s
FROM   (SELECT MAX(position)+1 FROM samd.NGrams8k(@string,1) WHERE token = '\') AS f(p)
CROSS 
APPLY  (VALUES(SUBSTRING(@string,f.p,CHARINDEX('.',@string,f.p)-f.p+5)))  AS e(s);

-- John C
with   cte1(N)   As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
       cte2(N)   As (Select Top (IsNull(DataLength(@String),0)) Row_Number() over (Order By (Select NULL)) From (Select N=1 From cte1 N1,cte1 N2,cte1 N3,cte1 N4,cte1 N5,cte1 N6) A ),
       cte3(N)   As (Select 1 Union All Select t.N+DataLength(@Delimiter1) From cte2 t Where Substring(@String,t.N,DataLength(@Delimiter1)) = @Delimiter1),
       cte4(N,L) As (Select S.N,IsNull(NullIf(CharIndex(@Delimiter1,@String,s.N),0)-S.N,8000) From cte3 S)

Select RetSeq = Row_Number() over (Order By N)
      ,RetPos = N
      ,RetVal = left(RetVal,charindex(@Delimiter2,RetVal)-1) 
 From  (
        Select *,RetVal = Substring(@String, N, L) 
         From  cte4
       ) A
 Where charindex(@Delimiter2,RetVal)>1;

现在是执行计划:

【讨论】:

  • 令人印象深刻的比较和洞察力。给我一些(重新)思考的东西。 +1
【解决方案2】:

有几种方法可以做到这一点。这是一种方法

select substring(reverse(substring(reverse('C:\cloud\files\1191_f12.xlxb_12.16 test - 0.3 ML'),1,charindex('\',reverse('C:\cloud\files\1191_f12.xlxb_12.16 test - 0.3 ML'))-1)),1,13)

如果您的文件名并不总是相同,您可以使用 _ 执行相同的逻辑

这将与下划线一起使用 - 因为除了 xlxb_ 之外没有其他下划线

select 
reverse(substring(reverse(reverse(substring(reverse('C:\cloud\files\1191_f12.xlxb_12.16 test - 0.3 ML'),1,charindex('\',reverse('C:\cloud\files\1191_f12.xlxb_12.16 test - 0.3 ML'))-1))),charindex('_',reverse('C:\cloud\files\1191_f12.xlxb_12.16 test - 0.3 ML'))+1,len('C:\cloud\files\1191_f12.xlxb_12.16 test - 0.3 ML')))

【讨论】:

  • 我们有什么办法可以做到这一点,这将适用于整个表格列,而不仅仅是特定的字符串?谢谢
  • @mrya 用你的列名替换字符串
【解决方案3】:

对于此类任务,最好实现 SQL CLR 功能。这是example 如何创建这样的。有了正则表达式支持,您可以执行以下操作:

DECLARE @DataSource TABLE
(
    [value] VARCHAR(256)
);

INSERT INTO @DataSource ([value])
VALUES ('C:\filedata\6860_f11.xlxb_3.30 test - 0.3 ML')
      ,('C:\cloud\files\1191_f12.xlxb_12.16 test - 0.3 ML');

SELECT *
FROM @DataSource DS
CROSS APPLY [dbo].[fn_Utils_RegexMatches] ([value], '[^\\]+\.xlxb');

当然,您可以创建更复杂的模式来使用正则表达式进行匹配。

【讨论】:

    【解决方案4】:

    如果对表值函数开放,请考虑以下事项。

    厌倦了提取字符串(左、右、charindex、patindex 等),我修改了一个解析函数以接受两个不相似的分隔符。在这种情况下'\''.xlxb'

    作为 TVF,它很容易融入 CROSS APPLY。

    示例

    Declare @YourTable table ([file] varchar(150))
    Insert Into @YourTable values
     ('C:\filedata\6860_f11.xlxb_3.30 test - 0.3 ML')
    ,('C:\cloud\files\1191_f12.xlxb_12.16 test - 0.3 ML')
    
    Select A.*
          ,B.*
     From  @YourTable A
     Cross Apply [dbo].[tvf-Str-Extract](A.[File],'\','.xlxb') B
    

    退货

    file                                                RetSeq  RetPos  RetVal
    C:\filedata\6860_f11.xlxb_3.30 test - 0.3 ML        1       13      6860_f11
    C:\cloud\files\1191_f12.xlxb_12.16 test - 0.3 ML    1       16      1191_f12
    

    有兴趣的 TVF

    CREATE FUNCTION [dbo].[tvf-Str-Extract] (@String varchar(max),@Delimiter1 varchar(100),@Delimiter2 varchar(100))
    Returns Table 
    As
    Return (  
    
    with   cte1(N)   As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
           cte2(N)   As (Select Top (IsNull(DataLength(@String),0)) Row_Number() over (Order By (Select NULL)) From (Select N=1 From cte1 N1,cte1 N2,cte1 N3,cte1 N4,cte1 N5,cte1 N6) A ),
           cte3(N)   As (Select 1 Union All Select t.N+DataLength(@Delimiter1) From cte2 t Where Substring(@String,t.N,DataLength(@Delimiter1)) = @Delimiter1),
           cte4(N,L) As (Select S.N,IsNull(NullIf(CharIndex(@Delimiter1,@String,s.N),0)-S.N,8000) From cte3 S)
    
    Select RetSeq = Row_Number() over (Order By N)
          ,RetPos = N
          ,RetVal = left(RetVal,charindex(@Delimiter2,RetVal)-1) 
     From  (
            Select *,RetVal = Substring(@String, N, L) 
             From  cte4
           ) A
     Where charindex(@Delimiter2,RetVal)>1
    
    )
    /*
    Max Length of String 1MM characters
    
    Declare @String varchar(max) = 'Dear [[FirstName]] [[LastName]], ...'
    Select * From [dbo].[tvf-Str-Extract] (@String,'[[',']]')
    */
    

    【讨论】:

      【解决方案5】:

      对于 SQL Server,从 2016 年开始,您可以使用 STRING_SPLIT():

      CREATE TABLE #FileTable (
         FullName varchar(150)
      )
      INSERT INTO #FileTable (FullName)
      VALUES
          ('C:\filedata\6860_f11.xlxb_3.30 test - 0.3 ML'),
          ('C:\cloud\files\1191_f12.xlxb_12.16 test - 0.3 ML')
      
      SELECT 
          f.FullName, 
          SUBSTRING(s.[value], 1, CHARINDEX('.xlxb', s.[value]) + LEN('xlxb')) AS FileName
      FROM #FileTable f
      CROSS APPLY STRING_SPLIT(f.FullName, '\') s 
      WHERE s.[value] LIKE '%.xlxb%'
      

      输出:

      FullName                                            FileName
      C:\filedata\6860_f11.xlxb_3.30 test - 0.3 ML        6860_f11.xlxb
      C:\cloud\files\1191_f12.xlxb_12.16 test - 0.3 ML    1191_f12.xlxb
      

      【讨论】:

        猜你喜欢
        • 2017-12-27
        • 1970-01-01
        • 2018-05-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多