如何使用CharIndex / SubString获取第三个字符串部分

时间:2019-03-28 15:24:36

标签: sql sql-server tsql

每个人都试图将名称列分成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]

我想摆脱第三列中的jr。目的是要有4个不同的列,其中名称的4个不同部分。

4 个答案:

答案 0 :(得分:1)

第三个字符串中出现“ 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相同的技术。

答案 1 :(得分: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 :(得分:1)

这是您想要的吗?

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

答案 3 :(得分:0)

使用拆分功能,可以很简单地安排它。

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  
;