【问题标题】:TSQL: Find a date with varying characters in a stringTSQL:在字符串中查找具有不同字符的日期
【发布时间】:2019-12-13 22:21:44
【问题描述】:

我需要从列名 Filename 的字符串中找到一个连续的日期。该字符串中包含其他带有破折号的数字(或其他字符,例如下划线),但我只需要连续数字

需要从文件名中提取日期。 (我知道数据只是哇,多个供应商,多种文件命名格式是原因。)

这个问题与这个问题类似,但它正在寻找具有不同要求的不同内容:TSQL: Find a continuous number in a string

期望的结果:

实际结果:

测试代码:

DROP TABLE #dob

CREATE TABLE #dob (
 FILENAME VARCHAR(MAX)
,StudentID INT
)

INSERT INTO #dob
( FILENAME  )
VALUES
 ('Smith John D, 11-23-1980, 1234567.pdf')
,('Doe Jane, _01_22_1980_123456.pdf')
,('John Doe, 567891.pdf' )

--This is what I tried.

SELECT FILENAME
, substring(FileName, patindex('%[0-9][%-%][%_%][0-9][0-9][0-9][0-9][0-9]%', FileName), 8) AS dob
FROM #dob

【问题讨论】:

    标签: sql sql-server tsql sql-server-2016


    【解决方案1】:

    试试这样:

    DROP TABLE #StuID
    GO
    CREATE TABLE #StuID (
     FILENAME VARCHAR(MAX)
    ,StudentID INT
    )
    
    INSERT INTO #StuID
    ( FILENAME  )
    VALUES
     ('Smith John D, 11-23-1980, 1234567.pdf')
    ,('Doe Jane, _01_22_1980_123456.pdf')
    ,('John Doe, 567891.pdf' );
    
    WITH Casted([FileName],ToXml) AS
    (
        SELECT [FILENAME] 
              ,CAST('<x>' + REPLACE(REPLACE(REPLACE(REPLACE(REPLACE([FILENAME],'.',' '),'-',' '),'_',' '),',',' '),' ','</x><x>') + '</x>' AS XML)
        FROM #StuID
    )
    SELECT [FileName] 
          ,numberFragments.value('/x[.>=1 and .<=31][1]','int') AS MonthFragment --using <=12 might bring back the second fragment twice...
          ,numberFragments.value('/x[.>=1 and .<=31][2]','int') AS DayFragment
          ,numberFragments.value('/x[.>=1960 and .<=2050][1]','int') AS YearFragment
          ,numberFragments.value('/x[.>=100000 and .<=10000000][1]','int') AS StudId
    FROM Casted
    CROSS APPLY (SELECT ToXml.query('/x[not(empty(. cast as xs:int?))]')) A(numberFragments);
    

    简而言之:

    与上一个答案一样,我们将字符串分解为 XML 并过滤可转换为 int 的片段。
    XQuery 过滤的神奇之处:

    • 我们选择 1 到 31 之间的第一个片段,希望是月份
    • 我们选择 1 到 31 之间的第二个片段,这是希望的日子
    • 我们选择 1960 年到 2050 年之间的第一个片段,这是希望的一年
    • 我们选择学生的 id,它是 100000 到 10000000 之间的第一个片段。

    提示:将&lt;=12 用于月份片段看起来是个好主意,但我会为日期和月份使用相同的过滤器以确保我们选择first和同值区域的第二个片段...

    【讨论】:

    • 谢谢@Shnugo!我不知道 XQuery。似乎很强大。除了打断琴弦之外,您是否经常使用它?如果是这样,你用它做什么典型的事情? (不确定这是不是问这个问题的好地方,如果不是,请告诉我一个好地方在哪里。)再次感谢。
    • @JM1 好吧,不,评论不是详细了解 XQuery 的最佳位置 :-)。这在您必须处理 XML 时经常使用。除了显而易见的,它还允许一些非常有用的 hack,在这些 hack 中,您以某种方式滥用 XML 处理一般数据的能力。您可以read some of my XQuery related answers 感受一下...
    • @JM1 另一种用法是创建一个未知集合,并将其作为 XML 参数传递给函数或存储过程。在this answer 中,我提供了一种将任何 SELECT 转换为 HTML 表的方法。一般来说,有人可能会说:这总是一种 hack,它的性能不会很好,但它允许做你可能说不可能的事情 :-)
    【解决方案2】:

    我认为你的模式不太正确。此外,您可以使用CASE 表达式返回NULL

    SELECT FILENAME,
           (CASE WHEN FileName LIKE '%[0-9][0-9][-_][0-9][0-9][-_][0-9][0-9][0-9][0-9]%'
                 THEN substring(FileName, patindex('%[0-9][0-9][-_][0-9][0-9][-_][0-9][0-9][0-9][0-9]%', FileName), 10)
            END) AS dob
    FROM #dob;
    

    你也可以不用CASE而使用NULLIF()

       substring(FileName, NULLIF(patindex('%[0-9][0-9][-_][0-9][0-9][-_][0-9][0-9][0-9][0-9]%', FileName), 0), 10) as dob
    

    【讨论】:

    • 谢谢@GordonLinoff。这是一个优雅的解决方案,也很有效。感谢您的帮助!
    • @JM1 。 . .这比其他解决方案简单得多。
    【解决方案3】:

    另一种方法是(在使用PATINDEX 查找日期之后)将字符串的格式强制为MM/dd/yyyy,然后使用显式样式进行转换:

    SELECT *,
           TRY_CONVERT(date,STUFF(STUFF(SUBSTRING(d.FILENAME,V.I, 10),3,1,'/'),6,1,'/'),101)
    FROM #dob d
         CROSS APPLY (VALUES(NULLIF(PATINDEX('%[0-9][0-9]_[0-9][0-9]_[0-9][0-9][0-9][0-9]%',d.[FILENAME]),0))) V(I);
    

    【讨论】:

    • 谢谢@Larnu!此解决方案重新格式化日期。感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 2012-04-09
    • 2019-12-13
    • 2011-09-27
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    • 2012-10-24
    • 1970-01-01
    相关资源
    最近更新 更多