SQL Server 正在提取字符串中的最后一个数字

lyfkaqu1  于 2023-01-25  发布在  其他
关注(0)|答案(5)|浏览(364)

我有下面的示例数据,我试图提取字符串中的最后一个数字,我有下面的示例,这让我有了一部分的方法,但我不知道如何获得它不是最后一个单词的值。

right(TextDescription, patindex('%[^0-9]%',reverse(TextDescription)) - 1)

结果应为:
| 识别号|电码|
| - ------|- ------|
| 1个|小行星1001|
| 第二章|小行星100040344|
| 三个|小行星10015370|
| 四个|零|
| 五个|小行星400337|
样本数据

Create Table #TestData
(
    ID int,
    TextDescription varchar(100)
)

insert into #TestData Values (1,'Data From JOE BLOGGS 10015662 tree 10015662')
insert into #TestData Values (2,'Fast Data From JOHN SMITH 10004034 MARY SMITH 100040344 plant')
insert into #TestData Values (3,'Data In 10015370 pot JONES')
insert into #TestData Values (4,'Fast Data From LEE tree')
insert into #TestData Values (5,'Direct Data 106600 JANE GREEN 400337')
xtupzzrd

xtupzzrd1#

这是另一个使用JSON的选项,它将字符串转换为数组,[key]将保持序列。

Select A.ID 
      ,B.Value
 From  #TestData A
 Outer Apply (
                Select top 1 Value
                 From  OpenJSON( '["'+replace(string_escape(TextDescription,'json'),' ','","')+'"]' )
                 Where try_convert(int,Value) is not null
                 Order by [key] Desc
             ) B
    • 结果**
ID  Value
1   10015662
2   100040344
3   10015370
4   NULL
5   400337
wgxvkvu9

wgxvkvu92#

下面的代码将定位最后一个数字,在该点修剪字符串,找到前面的非数字,然后执行另一次修剪以获得最终结果。正如在您的原始帖子中一样,计算是在反转的字符串上完成的,以适应LASTPATINDEX()函数的缺失。CROSS APPLY用于构建中间结果并避免子表达式的重复。

select T.*, P1.Pos1, P2.Pos2, N.Result
from #TestData T
cross apply (select reverse(TextDescription) AS Reversed) R
cross apply (select nullif(patindex('%[0-9]%', R.Reversed), 0) AS Pos1) P1
cross apply (select stuff(R.Reversed, 1, P1.Pos1 - 1, '') AS Trim1) T1
cross apply (select patindex('%[^0-9]%', T1.Trim1 + 'X') AS Pos2) P2
cross apply (select reverse(left(T1.Trim1, P2.Pos2 - 1)) AS Result) N

-- Partly reduced
select T.*, reverse(left(T1.Trim1, patindex('%[^0-9]%', T1.Trim1 + 'X') - 1)) AS Result
from #TestData T
cross apply (select reverse(TextDescription) AS Reversed) R
cross apply (select stuff(R.Reversed, 1, nullif(patindex('%[0-9]%', R.Reversed), 0) - 1, '') AS Trim1) T1

这将处理各种形式,而不仅仅是空格分隔的值。
参见this db<>fiddle

bq9c1y66

bq9c1y663#

如果您感兴趣的值总是出现在末尾,并且总是以非数字开头,则可以将SUBSTRING与以下语句配合使用:

  • 下限是最后一个数字位置之前的最后一个非数字
  • 长度是最后一个非数字和最后一个数字的第一个值之间的差
WITH cte AS (
    SELECT ID, TextDescription,
           PATINDEX('%[0-9][^0-9]%', REVERSE(TextDescription) + ' ') AS first_space,
           PATINDEX('%[0-9]%'      , REVERSE(TextDescription)      ) AS last_digit 
    FROM #TestData
)  
SELECT ID, 
       SUBSTRING(TextDescription, 
                 LEN(TextDescription) -first_space +1,
                 first_space+1 -last_digit)              AS code
FROM cte

检查here演示。

blpfk2vs

blpfk2vs4#

请尝试以下利用XML和XQuery的解决方案。
值得注意的要点:

  • CROSS APPLY正在将 * TextDescription * 列标记为XML。
  • XQuery FLWOR表达式正在检查每个标记是否可以转换为INTEGER数据类型。如果不能,则将其过滤掉。
  • XPath predicate [last()]正在为我们提供最后一个INTEGER值。
    • SQL语言**
-- DDL and sample data population, start
DECLARE @tbl Table (ID INT IDENTITY PRIMARY KEY, TextDescription varchar(100));
INSERT INTO @tbl (TextDescription) VALUES 
('Data From JOE BLOGGS 10015662 tree 10015662'),
('Fast Data From JOHN SMITH 10004034 MARY SMITH 100040344 plant'),
('Data In 10015370 pot JONES'),
('Fast Data From LEE tree'),
('Direct Data 106600 JANE GREEN 400337');
-- DDL and sample data population, end

DECLARE @separator CHAR(1) = SPACE(1);

SELECT t.*
    , c.query('for $x in /root/r[not(empty(xs:int(.)))]
        return $x
    ').value('(/r[last()]/text())[1]','INT') AS [code]
FROM @tbl AS t
CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' + 
      REPLACE(TextDescription, @separator, ']]></r><r><![CDATA[') + 
      ']]></r></root>' AS XML)) AS t1(c)
ORDER BY t.ID;
    • 产出**

| 识别号|文本描述|电码|
| - ------|- ------|- ------|
| 1个|数据来自JOE博客10015662树10015662|小行星1001|
| 第二章|来自约翰·史密斯10004034玛丽·史密斯100040344工厂的快速数据|小行星100040344|
| 三个|10015370罐JONES中的数据|小行星10015370|
| 四个|LEE树快速数据|零|
| 五个|直接数据106600简·格林400337|小行星400337|

92dk7w1h

92dk7w1h5#

CREATE FUNCTION [ExtractInteger](@String VARCHAR(2000))
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE @Count INT
DECLARE @IntNumbers VARCHAR(1000)
SET @Count = 0
SET @IntNumbers = ''

WHILE @Count <= LEN(@String)
BEGIN

IF SUBSTRING(@String,@Count,1) = ' '
BEGIN
SET @IntNumbers = @IntNumbers + ' ' 
END

IF SUBSTRING(@String,@Count,1) >= '0'
AND SUBSTRING(@String,@Count,1) <= '9'
BEGIN
SET @IntNumbers = @IntNumbers + SUBSTRING(@String,@Count,1)
END
SET @Count = @Count + 1
END

RETURN LTRIM(RTRIM(@IntNumbers))
END

ExtractInteger函数将只获取数字和空格,下面的select将把最后一个字作为数字:

select right(dbo.ExtractInteger('My 3rd Phone Number is 323-111-CALL'), charindex(' ', reverse(dbo.ExtractInteger('My 3rd Phone Number is 323-111-CALL')) + ' ') - 1)

相关问题