【问题标题】:How to get 3rd string part with CharIndex/SubString [duplicate]如何使用 CharIndex/SubString 获取第三个字符串部分 [重复]
【发布时间】:2019-08-19 10:38:29
【问题描述】:

我试图将名称列分成 4 个不同部分的每个人。截至目前,所有名称部分都由空格''分隔。我的@thirdString 无法填充名称的第四部分(通常是后缀),我想将其视为@fourthString。我将使用不同长度的不同名称运行它。我只是以 Robert Dobson Bud jr 为例。其他名称可以是两部分或更多部分。

-- Code for parsing a name with multiple parts
-- You should be able to copy and paste this into any MS-SQL Environment it doesn't use a certain table.

DECLARE     @nameString as varchar(max),
        @firstSpaceLoc as smallint,
        @secondSpaceLoc as smallint,
        @thirdSpaceLoc as smallint,
        @forthSpaceLoc as smallint,
        @firstString as varchar(max),
        @secondString as varchar(max),
        @thirdString as varchar(max),
        @fourthString as varchar(max) 


 -- Create some type of loop or case statement to run through the entire table. 
SET @nameString = 'Robert Dobson Bud jr'

SET @firstSpaceLoc = CHARINDEX(' ',@namestring,1)

SET @secondSpaceLoc = CHARINDEX(' ', @namestring, CHARINDEX(' ',@nameString,1)+1)

SET @thirdSpaceLoc = 
CASE
  WHEN CHARINDEX(' ', 
                          @namestring, 
                          CHARINDEX(' ',@nameString,1)+1) = 0 THEN 0
  WHEN CHARINDEX(' ', 
                          @namestring, 
                          CHARINDEX(' ',@nameString,1)+1) > 0 THEN
                                CHARINDEX(' ', @namestring, 
                                CHARINDEX(' ', @namestring, 
                                CHARINDEX(' ',@nameString,1)+1)+1)
END
SET @forthSpaceLoc =
CASE
WHEN CHARINDEX(' ', 
                          @namestring, 
                          CHARINDEX(' ',@nameString,1)+1) = 0 THEN 0
WHEN CHARINDEX(' ', 
                          @namestring, 
                          CHARINDEX(' ',@nameString,1)+1) > 0 THEN 0
WHEN CHARINDEX(' ', 
                          @namestring, 
                          CHARINDEX(' ',@nameString,1)+1) > 0 THEN
                            CHARINDEX(' ', 
@namestring, 
                                CHARINDEX(' ', @namestring,

CHARINDEX(' ', @nameString, 
                                CHARINDEX(' ',@nameString,1)+1)+1)+1)
END

SELECT

   @firstString = 
   CASE
        WHEN @firstSpaceLoc > 0 THEN LEFT(@nameString,CHARINDEX(' ',@namestring,1)-1)
        ELSE @nameString
   END,
   @secondString =   
   CASE
        WHEN @firstSpaceLoc = 0 THEN ''
        WHEN @secondSpaceLoc = 0 THEN 
                    RIGHT(@namestring, LEN(@namestring)- CHARINDEX(' ',@namestring,1))
        WHEN @secondSpaceLoc > 0 THEN
                    REPLACE     (
                    SUBSTRING   (
                                   @nameString, CHARINDEX(' ',@namestring,1)+1, CHARINDEX(' ', @namestring, CHARINDEX(' ',@nameString,1)+1) 
                                         - CHARINDEX(' ',@namestring,1)),' ',''
                                 )
        ELSE ''
  END,



                                     @thirdString =
  CASE
        WHEN @firstSpaceLoc = 0 OR @secondSpaceLoc = 0  THEN ''
        WHEN @secondSpaceLoc > 0 THEN
                    SUBSTRING   (
                                   @nameString,
                                   CHARINDEX(' ', @namestring, 
                                   CHARINDEX(' ',@nameString,1)+1),
                                   LEN(@nameString)
                                 )             
  END,
  @fourthString =
  CASE
        WHEN @firstSpaceLoc = 0 OR @secondSpaceLoc = 0 OR @thirdSpaceLoc = 0 THEN ''
        WHEN @secondSpaceLoc > 0 AND @thirdSpaceLoc = 0 THEN ''
        WHEN @thirdSpaceLoc > 0 THEN
                        SUBSTRING( 
                                    @nameString,
                                   CHARINDEX(' ', @namestring,
                                   CHARINDEX(' ', @namestring, 
                                   CHARINDEX(' ',@nameString,1)+1)+1),
                                   LEN(@nameString)
                                    )

 END

-- Report names
SELECT
        @nameString sourceString,
        @firstString [First string],
        @secondString [Second string],
        @thirdString [Third string],
        @fourthString [Fourth String]

我想去掉第 3 列中的 jr。目的是拥有 4 个不同的列,其中包含名称的 4 个不同部分。

【问题讨论】:

标签: sql sql-server tsql


【解决方案1】:

这个脚本可以完成这项工作

DECLARE     @namestring as varchar(max)
        SET @namestring = 'Robert Dobson Bud jr'
        --SET @namestring = 'Robert Dobson'

        ;with cte as (
            select cast(0 as int) [start],CHARINDEX(' ',@namestring,0) [end] ,@namestring namestring
            union all
            select cast(cte.[end] as int) [start],CHARINDEX(' ',@namestring,cte.[end]+1) [end] ,@namestring namestring from cte where [end]>0
        ),cte2 as (
        select * ,ROW_NUMBER() over (order by cte.[start]) seq
        ,substring(@namestring,cte.[start]+1,(case when cte.[end]=0 then len(@namestring)+1 else cte.[end] end)-cte.[start]-1) part from cte
        )
        select 
         (select part from cte2 where seq=1) [First String]
        ,(select part from cte2 where seq=2) [Second String]
        ,(select part from cte2 where seq=3) [Third String]
        ,(select part from cte2 where seq=4) [Fourt String]

4 部分名称的结果如下

First String    Second String   Third String    Fourt String
Robert          Dobson          Bud         jr

对于 2 部分名称结果将如下所示

First String    Second String   Third String    Fourt String
Robert          Dobson          NULL            NULL

【讨论】:

    【解决方案2】:

    您在第三个字符串中得到“jr”的原因有点令人费解。它在这部分代码中:

    @thirdString = CASE
        WHEN @firstSpaceLoc = 0 OR @secondSpaceLoc = 0  THEN ''
        WHEN @secondSpaceLoc > 0 THEN
                    SUBSTRING   (
                                   @nameString,
                                   CHARINDEX(' ', @namestring, 
                                   CHARINDEX(' ',@nameString,1)+1),
                                   LEN(@nameString)
                                 )             
    

    您为什么使用LEN(@nameString) 作为SUBSTRING 的第三个参数?当然,这将返回字符串的其余部分,包括“Jr”。获取@secondString 值时明明知道不这样做,获取@thirdString 时怎么不知道这样做?

    要获取@thirdString,您需要使用与获取@secondString 相同的技术。

    【讨论】:

    • 是的先生,我知道这就是问题所在,但是当我尝试应用与在 secondString 中相同的逻辑时,它并没有正确显示。我得到了一些 secondString 文本,或者只是一般不正确的格式
    • 您应该编辑您的问题以显示您尝试在第三个字符串中使用相同的逻辑,以便我们可以调试它为什么不适合您。顺便说一句,您可以通过使用变量而不是重复它们的计算来使您的代码更易于阅读。换句话说,您可以简单地使用@firstSpaceLoc+1 而不是CHARINDEX(' ',@nameString,1)+1
    【解决方案3】:

    这是你想要的吗?

    DECLARE @Str VARCHAR(45) = 'Robert Dobson Bud jr';
    
    WITH CTE AS
    (
      SELECT Value V,
             'Str' + CAST(ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS VARCHAR(10)) RN
      FROM STRING_SPLIT(@Str, ' ')
    )
    SELECT *
    FROM
         (
           SELECT *
           FROM CTE
         ) X
         PIVOT
         (
           MAX(V) FOR RN IN ([Str1], [Str2], [Str3], [Str4])
         ) P;
    

    返回:

    +--------+--------+------+------+
    |  Str1  |  Str2  | Str3 | Str4 |
    +--------+--------+------+------+
    | Robert | Dobson | Bud  | jr   |
    +--------+--------+------+------+
    

    Live Demo

    【讨论】:

      【解决方案4】:

      使用拆分功能,这可以很简单的安排。

      SELECT firstString  = MAX(CASE WHEN ItemNumber = 1 THEN Item END),
             secondString = MAX(CASE WHEN ItemNumber = 2 THEN Item END),
             thirdString  = MAX(CASE WHEN ItemNumber = 3 THEN Item END),
             fourthString = MAX(CASE WHEN ItemNumber = 4 THEN Item END)
      FROM dbo.DelimitedSplit8K_LEAD( @nameString, ' ');
      

      函数的代码最初发布并解释here。但我在复制定义。

      CREATE FUNCTION [dbo].[DelimitedSplit8K_LEAD]  
      --===== Define I/O parameters  
              (@pString VARCHAR(8000), @pDelimiter CHAR(1))  
      RETURNS TABLE WITH SCHEMABINDING AS  
       RETURN  
      --===== "Inline" CTE Driven "Tally Table” produces values from 0 up to 10,000...  
           -- enough to cover VARCHAR(8000)  
       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  
      ;  
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-11-15
        • 1970-01-01
        • 2017-07-28
        • 2021-08-13
        • 2014-12-20
        • 1970-01-01
        • 2016-11-07
        • 2013-09-24
        相关资源
        最近更新 更多