TSQL:解析具有各种字符的字符串

Posted

技术标签:

【中文标题】TSQL:解析具有各种字符的字符串【英文标题】:TSQL: Parsing strings with various characters 【发布时间】:2019-08-16 16:10:59 【问题描述】:

我有一个带有文件名列的表,其中各个供应商以不同的方式命名文件。因此,文件中存在一个文件名,其中包含姓氏、名字和中间名,并用各种字符分隔名称。有些有逗号+空格,逗号不带空格,单词之间有空格,单词之间没有空格,一个下划线,两个下划线等等。

有什么好的方法可以提取到想要的结果?(这是一次性的数据转换,不一定要漂亮。)

我在下面的示例代码中尝试过,使用各种子字符串/字符索引组合

文件名示例:(注意逗号、空格、无空格、下划线、双下划线)

期望的结果:

示例代码/测试数据(在临时表中)

   IF OBJECT_ID('tempdb..#dob') IS NOT NULL DROP TABLE #dob

   CREATE TABLE #dob (
    FILENAME VARCHAR(MAX)
   ,StudentID INT
   ,FullName VARCHAR(500)
   ,LastName VARCHAR(500)
   ,FirstName VARCHAR(500)
   ,MiddleName VARCHAR(500)
   )

   INSERT INTO #dob
   ( FILENAME  )
   VALUES
    ('Last, First, Middle_DOB ID.pdf')
   ,('Denver, John C 11_23_1980_123456.pdf')
   ,('Denver John_11-23-1980, 1234567.pdf')
   ,('Denver,John,Clifford_ 01_22_1980_123456.pdf')
   ,('Denver, John, 11-23-1980, 1234567.pdf')
   ,('Denver, John__01_22_1980_123456.pdf')

   --This is what I tried.

   SELECT FILENAME
     ,fullname
     ,LastName
     ,FirstName
     ,MiddleName
     ,SUBSTRING(FileName,1, CHARINDEX(' ', FileName, (charindex(' ', FileName, 1))+2)) AS test1
     ,SUBSTRING(FileName,1, CHARINDEX('_', FileName, (charindex(' ', FileName, 1))+2)) AS test2
     ,SUBSTRING(FileName,1, CHARINDEX(',', FileName, (charindex(', ', FileName, 1))+1)) AS test3
     ,SUBSTRING(FileName,1, CHARINDEX(' ', FileName, (charindex('__', FileName, 1))+2)) AS test4
     ,SUBSTRING(FileName,1, CHARINDEX('__', FileName, (charindex(' ', FileName, 1))+2)) AS test5


   FROM #dob

【问题讨论】:

这至少可以说是丑陋的。值得庆幸的是,这是一次性的事情,而不是一场持续的噩梦。您将不得不为此进行几次传递,因为格式无处不在。 【参考方案1】:

这是一个滑坡,但如果您的真实数据接近样本,请考虑以下事项。

示例

   SELECT FILENAME
         ,LastName   = Pos1
         ,FirstName  = Pos2
         ,MiddleName = case when try_convert(int,left(Pos3,1)) is null then Pos3 else '' end
   FROM #dob A
   Cross Apply ( values ( replace(
                          replace(
                          replace(
                          replace(FileName,', ',',')
                          ,' ,',',')
                          ,' ',',')
                          ,'_',',')
                        )
               ) B(CleanString)
   Cross Apply [dbo].[tvf-Str-Parse-Row](CleanString,',') C

退货

FILENAME                                      LastName  FirstName   MiddleName
Last, First, Middle_DOB ID.pdf                Last      First       Middle
Denver, John C 11_23_1980_123456.pdf          Denver    John        C
Denver John_11-23-1980, 1234567.pdf           Denver    John    
Denver,John,Clifford_ 01_22_1980_123456.pdf   Denver    John        Clifford
Denver, John, 11-23-1980, 1234567.pdf         Denver    John    
Denver, John__01_22_1980_123456.pdf           Denver    John    

TVF(如果有兴趣)

CREATE FUNCTION [dbo].[tvf-Str-Parse-Row] (@String varchar(max),@Delimiter varchar(10))
Returns Table 
As
Return (
    Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
          ,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
          ,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
          ,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
          ,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
          ,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(max)')))
          ,Pos7 = ltrim(rtrim(xDim.value('/x[7]','varchar(max)')))
          ,Pos8 = ltrim(rtrim(xDim.value('/x[8]','varchar(max)')))
          ,Pos9 = ltrim(rtrim(xDim.value('/x[9]','varchar(max)')))
    From  ( values (cast('<x>' + replace((Select replace(@String,@Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml))) as A(xDim)
)

【讨论】:

@JM1 Cross Apply B 将“清理字符串”,这可能需要随着时间的推移进行调整 @JM1 Cross Apply C 将调用表值函数。 TVF将字符串转换成XML,然后会生成9列, @JM1 顶部选择只是应用了一点逻辑来确定是否有 2 个或 3 个名称 @JM1 乐于助人。完全披露,我是 XML 的后期采用者。值得你花时间,另外,如果 2016+,JSON :) @JM1 这是 XML 而不是 JSON。 JSON 于 2016 年推出。要学习 XML 部分,我会谷歌“SQL Server XML 教程”或从 mssqltips.com/sqlservertip/2889/basic-sql-server-xml-querying 开始【参考方案2】:

这是我尝试使用 patindex 的东西,看看是否有帮助

SELECT FILENAME
    ,fullname
    ,LastName
    ,FirstName
    ,MiddleName
    ,ISNULL(LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)-1),'')
    +' '
    +ISNULL(LEFT(LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')),PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))-1),'')
    +' '
    +ISNULL(IIF(PATINDEX('%[, _]%',LEFT(LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')),PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')))))>1
    ,IIF(PATINDEX('%[a-z]%',LEFT(LEFT(LEFT(LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')),PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')))),PATINDEX('%[, _]%',LEFT(LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')),PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')))))),1))=1,
    LEFT(LEFT(LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')),PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')))),PATINDEX('%[, _]%',LEFT(LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')),PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')))))-1)
    ,NULL)
    ,NULL),'') FULLNAME
    ,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)-1) LASTNAME
    ,LEFT(LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')),PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))-1) FIRSTNAME
    ,IIF(PATINDEX('%[, _]%',LEFT(LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')),PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')))))>1
    ,IIF(PATINDEX('%[a-z]%',LEFT(LEFT(LEFT(LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')),PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')))),PATINDEX('%[, _]%',LEFT(LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')),PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')))))),1))=1,
    LEFT(LEFT(LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')),PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')))),PATINDEX('%[, _]%',LEFT(LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')),PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)+PATINDEX('%[, _]%',LTRIM(REPLACE(FILENAME,LEFT(FILENAME,PATINDEX('%[, _]%',FILENAME)),'')))),'')))))-1)
    ,NULL)
    ,NULL) MIDDLENAME
FROM #dob

【讨论】:

感谢您的宝贵时间,这确实按要求输出。

以上是关于TSQL:解析具有各种字符的字符串的主要内容,如果未能解决你的问题,请参考以下文章

路径包含有趣字符时的 TSQL OPENJSON

来自OPENJONS()的TSQL,具有显式架构:十进制(38,30)数据类型的空字符串

比较两个字符串的 TSQL 函数

TSQL 字符串函数:截断和查找

用于分组的 tsql 聚合字符串

TSQL:从字符串转换日期和/或时间时转换失败