86

我有这样的数据:

string 1: 003Preliminary Examination Plan   
string 2: Coordination005  
string 3: Balance1000sheet

我期望的输出是

string 1: 003
string 2: 005
string 3: 1000

我想在 SQL 中实现它。

4

18 回答 18

145

首先创建这个UDF

CREATE FUNCTION dbo.udf_GetNumeric
(
  @strAlphaNumeric VARCHAR(256)
)
RETURNS VARCHAR(256)
AS
BEGIN
  DECLARE @intAlpha INT
  SET @intAlpha = PATINDEX('%[^0-9]%', @strAlphaNumeric)
  BEGIN
    WHILE @intAlpha > 0
    BEGIN
      SET @strAlphaNumeric = STUFF(@strAlphaNumeric, @intAlpha, 1, '' )
      SET @intAlpha = PATINDEX('%[^0-9]%', @strAlphaNumeric )
    END
  END
  RETURN ISNULL(@strAlphaNumeric,0)
END
GO

现在使用functionas

SELECT dbo.udf_GetNumeric(column_name) 
from table_name

SQL 小提琴

我希望这能解决你的问题。

参考

于 2013-05-21T10:10:17.917 回答
58

试试这个——

询问:

DECLARE @temp TABLE
(
      string NVARCHAR(50)
)

INSERT INTO @temp (string)
VALUES 
    ('003Preliminary Examination Plan'),
    ('Coordination005'),
    ('Balance1000sheet')

SELECT LEFT(subsrt, PATINDEX('%[^0-9]%', subsrt + 't') - 1) 
FROM (
    SELECT subsrt = SUBSTRING(string, pos, LEN(string))
    FROM (
        SELECT string, pos = PATINDEX('%[0-9]%', string)
        FROM @temp
    ) d
) t

输出:

----------
003
005
1000
于 2013-05-21T10:28:04.693 回答
23

询问:

DECLARE @temp TABLE
(
    string NVARCHAR(50)
)

INSERT INTO @temp (string)
VALUES 
    ('003Preliminary Examination Plan'),
    ('Coordination005'),
    ('Balance1000sheet')

SELECT SUBSTRING(string, PATINDEX('%[0-9]%', string), PATINDEX('%[0-9][^0-9]%', string + 't') - PATINDEX('%[0-9]%', 
                    string) + 1) AS Number
FROM @temp
于 2016-08-11T02:49:30.930 回答
15

请试试:

declare @var nvarchar(max)='Balance1000sheet'

SELECT LEFT(Val,PATINDEX('%[^0-9]%', Val+'a')-1) from(
    SELECT SUBSTRING(@var, PATINDEX('%[0-9]%', @var), LEN(@var)) Val
)x
于 2013-05-21T10:13:10.977 回答
9

仅从字符串中获取数字可以在单行中完成。尝试这个 :

SUBSTRING('your-string-here', PATINDEX('%[0-9]%', 'your-string-here'), LEN('your-string-here'))

注意:仅适用于字符串中的第一个 int,例如:abc123vfg34 返回 123。

于 2020-01-08T21:33:25.703 回答
4

通过前面的查询,我得到了这些结果:

'AAAA1234BBBB3333' >>>> 输出:1234

'-çã+0!\aº1234' >>>> 输出:0

下面的代码返回所有数字字符:

第一个输出:12343333

第二个输出:01234

declare @StringAlphaNum varchar(255)
declare @Character varchar
declare @SizeStringAlfaNumerica int
declare @CountCharacter int

set @StringAlphaNum = 'AAAA1234BBBB3333'
set @SizeStringAlfaNumerica = len(@StringAlphaNum)
set @CountCharacter = 1

while isnumeric(@StringAlphaNum) = 0
begin
    while @CountCharacter < @SizeStringAlfaNumerica
        begin
            if substring(@StringAlphaNum,@CountCharacter,1) not like '[0-9]%'
            begin
                set @Character = substring(@StringAlphaNum,@CountCharacter,1)
                set @StringAlphaNum = replace(@StringAlphaNum, @Character, '')
            end
    set @CountCharacter = @CountCharacter + 1
    end
    set @CountCharacter = 0
end
select @StringAlphaNum
于 2014-12-19T15:54:07.990 回答
3
declare @puvodni nvarchar(20)
set @puvodni = N'abc1d8e8ttr987avc'

WHILE PATINDEX('%[^0-9]%', @puvodni) > 0 SET @puvodni = REPLACE(@puvodni, SUBSTRING(@puvodni, PATINDEX('%[^0-9]%', @puvodni), 1), '' ) 

SELECT @puvodni
于 2017-04-10T10:54:46.497 回答
2

此 UDF 适用于所有类型的字符串:

CREATE FUNCTION udf_getNumbersFromString (@string varchar(max))
RETURNS varchar(max)
AS
BEGIN
    WHILE  @String like '%[^0-9]%'
    SET    @String = REPLACE(@String, SUBSTRING(@String, PATINDEX('%[^0-9]%', @String), 1), '')
    RETURN @String
END
于 2020-02-07T15:18:32.733 回答
2

我没有创建功能的权利,但有类似的文字

["blahblah012345679"]

并且需要从中间提取数字

请注意,这假设数字组合在一起,而不是在字符串的开头和结尾。

select substring(column_name,patindex('%[0-9]%', column_name),patindex('%[0-9][^0-9]%', column_name)-patindex('%[0-9]%', column_name)+1)
from table name
于 2019-04-16T05:05:59.600 回答
1

虽然这是一个旧线程,它是谷歌搜索中的第一个,但我想出了一个与以前不同的答案。这将允许您通过您的标准来保存字符串中的内容,无论该标准可能是什么。如果你愿意,你可以把它放在一个函数中一遍又一遍地调用。

declare @String VARCHAR(MAX) = '-123.  a    456-78(90)'
declare @MatchExpression VARCHAR(255) = '%[0-9]%'
declare @return varchar(max)

WHILE PatIndex(@MatchExpression, @String) > 0
    begin
    set @return = CONCAT(@return, SUBSTRING(@string,patindex(@matchexpression, @string),1))
    SET @String = Stuff(@String, PatIndex(@MatchExpression, @String), 1, '')
    end
select (@return)
于 2019-06-28T14:19:03.720 回答
1

对@Epsicron 的回答稍作修改

SELECT SUBSTRING(string, PATINDEX('%[0-9]%', string), PATINDEX('%[0-9][^0-9]%', string + 't') - PATINDEX('%[0-9]%', 
                    string) + 1) AS Number
FROM (values ('003Preliminary Examination Plan'),
    ('Coordination005'),
    ('Balance1000sheet')) as a(string)

不需要临时变量

于 2019-06-04T11:50:43.863 回答
1

我发现这种方法的运行速度比投票率最高的答案快 3 倍。创建以下函数 dbo.GetNumbers:

CREATE FUNCTION dbo.GetNumbers(@String VARCHAR(8000))
RETURNS VARCHAR(8000)
AS
BEGIN;
    WITH
        Numbers
    AS (
        --Step 1.
        --Get a column of numbers to represent
        --every character position in the @String.
        SELECT 1 AS Number
        UNION ALL
        SELECT Number + 1
        FROM Numbers
        WHERE Number < LEN(@String)
        )
        ,Characters
    AS (
        SELECT Character
        FROM Numbers
        CROSS APPLY (
                --Step 2.
                --Use the column of numbers generated above
                --to tell substring which character to extract.
                SELECT SUBSTRING(@String, Number, 1) AS Character
            ) AS c
        )
    --Step 3.
    --Pattern match to return only numbers from the CTE
    --and use STRING_AGG to rebuild it into a single string.
    SELECT @String = STRING_AGG(Character,'')
    FROM Characters
    WHERE Character LIKE '[0-9]'

    --allows going past the default maximum of 100 loops in the CTE
    OPTION (MAXRECURSION 8000) 

    RETURN @String
END
GO

测试

测试目的:

SELECT dbo.GetNumbers(InputString) AS Numbers
FROM ( VALUES
         ('003Preliminary Examination Plan') --output: 003
        ,('Coordination005')                 --output: 005
        ,('Balance1000sheet')                --output: 1000
        ,('(111) 222-3333')                  --output: 1112223333
        ,('1.38hello@f00.b4r#\-6')           --output: 1380046
    ) testData(InputString)

性能测试:开始设置测试数据...

--Add table to hold test data
CREATE TABLE dbo.NumTest (String VARCHAR(8000)) 

--Make an 8000 character string with mix of numbers and letters
DECLARE @Num VARCHAR(8000) = REPLICATE('12tf56se',800)

--Add this to the test table 500 times
DECLARE @n INT = 0
WHILE @n < 500
BEGIN
    INSERT INTO dbo.NumTest VALUES (@Num)
    SET @n = @n +1
END

现在测试 dbo.GetNumbers 函数:

SELECT dbo.GetNumbers(NumTest.String) AS Numbers
FROM dbo.NumTest -- Time to complete: 1 min 7s

然后从相同数据的最高投票答案中测试 UDF 。

SELECT dbo.udf_GetNumeric(NumTest.String)
FROM dbo.NumTest -- Time to complete: 3 mins 12s

dbo.GetNumbers 的灵感

小数点

如果您需要它来处理小数,您可以使用以下任何一种方法,我发现它们之间没有明显的性能差异。

  • 更改'[0-9]''[0-9.]'
  • 更改Character LIKE '[0-9]'ISNUMERIC(Character) = 1(SQL 将单个小数视为数字)

奖金

WHERE Character LIKE '[0-9]'您可以通过更换以下选项轻松适应不同的要求:

  • WHERE Letter LIKE '[a-zA-Z]' --Get only letters
  • WHERE Letter LIKE '[0-9a-zA-Z]' --Remove non-alphanumeric
  • WHERE Letter LIKE '[^0-9a-zA-Z]' --Get only non-alphanumeric
于 2021-04-15T01:53:55.283 回答
0

这是最简单和最容易的一种。这也适用于多次出现的整个字符串。

CREATE FUNCTION dbo.fn_GetNumbers(@strInput NVARCHAR(500))
RETURNS NVARCHAR(500)
AS
BEGIN
DECLARE @strOut NVARCHAR(500) = '', @intCounter INT = 1
WHILE @intCounter <= LEN(@strInput) 
BEGIN
    SELECT @strOut = @strOut + CASE WHEN SUBSTRING(@strInput, @intCounter, 1) LIKE '[0-9]' THEN SUBSTRING(@strInput, @intCounter, 1) ELSE '' END    
    SET @intCounter = @intCounter + 1 

END
RETURN @strOut
END 

于 2021-12-06T00:06:32.783 回答
0

首先找出数字的起始长度,然后反转字符串以再次找出第一个位置(这将为您提供数字的结束位置)。现在,如果你从两个数字中减去 1 并从字符串全长中减去它,你将只得到数字长度。现在使用 SUBSTRING 获取号码

declare @fieldName nvarchar(100)='AAAA1221.121BBBB'

declare @lenSt int=(select PATINDEX('%[0-9]%', @fieldName)-1)
declare @lenEnd int=(select PATINDEX('%[0-9]%', REVERSE(@fieldName))-1)

select SUBSTRING(@fieldName, PATINDEX('%[0-9]%', @fieldName), (LEN(@fieldName) - @lenSt -@lenEnd))
于 2021-01-26T05:16:15.150 回答
0

T-SQL 函数从文本中读取所有整数并返回指定索引处的整数,从左或右开始,也使用起始搜索词(可选):

create or alter function dbo.udf_number_from_text(
    @text nvarchar(max),
    @search_term nvarchar(1000) = N'',
    @number_position tinyint = 1,
    @rtl bit = 0
) returns int
as
    begin
        declare @result int = 0;
        declare @search_term_index int = 0;

        if @text is null or len(@text) = 0 goto exit_label;
        set @text = trim(@text);
        if len(@text) = len(@search_term) goto exit_label;

        if len(@search_term) > 0
            begin
                set @search_term_index = charindex(@search_term, @text);
                if @search_term_index = 0 goto exit_label;
            end;

        if @search_term_index > 0
            if @rtl = 0
                set @text = trim(right(@text, len(@text) - @search_term_index - len(@search_term) + 1));
            else
                set @text = trim(left(@text, @search_term_index - 1));
        if len(@text) = 0 goto exit_label;

        declare @patt_number nvarchar(10) = '%[0-9]%';
        declare @patt_not_number nvarchar(10) = '%[^0-9]%';
        declare @number_start int = 1;
        declare @number_end int;
        declare @found_numbers table (id int identity(1,1), val int);

        while @number_start > 0
        begin
            set @number_start = patindex(@patt_number, @text);
            if @number_start > 0
                begin
                    if @number_start = len(@text)
                        begin
                            insert into @found_numbers(val)
                            select cast(substring(@text, @number_start, 1) as int);

                            break;
                        end;
                    else
                        begin
                            set @text = right(@text, len(@text) - @number_start + 1);
                            set @number_end = patindex(@patt_not_number, @text);

                            if @number_end = 0
                                begin
                                    insert into @found_numbers(val)
                                    select cast(@text as int);

                                    break;
                                end;
                            else
                                begin
                                    insert into @found_numbers(val)
                                    select cast(left(@text, @number_end - 1) as int);

                                    if @number_end = len(@text)
                                        break;
                                    else
                                        begin
                                            set @text = trim(right(@text, len(@text) - @number_end));
                                            if len(@text) = 0 break;
                                        end;
                                end;
                        end;
                end;
        end;

        if @rtl = 0
            select @result = coalesce(a.val, 0)
            from (select row_number() over (order by m.id asc) as c_row, m.val
                    from @found_numbers as m) as a
            where a.c_row = @number_position;
        else
            select @result = coalesce(a.val, 0)
            from (select row_number() over (order by m.id desc) as c_row, m.val
                    from @found_numbers as m) as a
            where a.c_row = @number_position;


        exit_label:
            return @result;
    end;

例子:

select dbo.udf_number_from text(N'Text text 10 text, 25 term', N'term',2,1);

返回 10;

于 2020-03-22T19:06:20.560 回答
-1

在甲骨文中

你可以用这个得到你想要的:

SUBSTR('ABCD1234EFGH',REGEXP_INSTR ('ABCD1234EFGH', '[[:digit:]]'),REGEXP_COUNT ('ABCD1234EFGH', '[[:digit:]]'))

示例查询:

SELECT SUBSTR('003Preliminary Examination Plan  ',REGEXP_INSTR ('003Preliminary Examination Plan  ', '[[:digit:]]'),REGEXP_COUNT ('003Preliminary Examination Plan  ', '[[:digit:]]')) SAMPLE1,
SUBSTR('Coordination005',REGEXP_INSTR ('Coordination005', '[[:digit:]]'),REGEXP_COUNT ('Coordination005', '[[:digit:]]')) SAMPLE2,
SUBSTR('Balance1000sheet',REGEXP_INSTR ('Balance1000sheet', '[[:digit:]]'),REGEXP_COUNT ('Balance1000sheet', '[[:digit:]]')) SAMPLE3 FROM DUAL
于 2019-10-08T12:30:43.630 回答
-2

对于它的地狱...

该解决方案不同于所有早期的解决方案,即:

  • 无需创建函数
  • 无需使用模式匹配
  • 不需要临时表
  • 此解决方案使用递归公用表表达式 (CTE)

但首先 - 请注意,该问题并未指定此类字符串的存储位置。在下面的解决方案中,我创建了一个 CTE 作为将这些字符串放入某种“源表”的快速而肮脏的方法。

另请注意 - 此解决方案使用递归公用表表达式(CTE) - 所以不要对这里使用两个 CTE 感到困惑。第一个只是使数据可用于解决方案 - 但解决此问题只需要第二个 CTE。您可以调整代码以使第二个 CTE 查询您现有的表、视图等。

最后 - 我的编码很冗长,尝试使用列和 CTE 名称来解释正在发生的事情,您也许可以稍微简化这个解决方案。我已经添加了一些伪电话号码,并带有一些(预期的和非典型的,视情况而定)格式,以获得它的乐趣。

with SOURCE_TABLE as (
    select '003Preliminary Examination Plan' as numberString
    union all select 'Coordination005' as numberString
    union all select 'Balance1000sheet' as numberString
    union all select '1300 456 678' as numberString
    union all select '(012) 995 8322  ' as numberString
    union all select '073263 6122,' as numberString
),
FIRST_CHAR_PROCESSED as (
    select
        len(numberString) as currentStringLength,
        isNull(cast(try_cast(replace(left(numberString, 1),' ','z') as tinyint) as nvarchar),'') as firstCharAsNumeric,
        cast(isNull(cast(try_cast(nullIf(left(numberString, 1),'') as tinyint) as nvarchar),'') as nvarchar(4000)) as newString,
        cast(substring(numberString,2,len(numberString)) as nvarchar) as remainingString
    from SOURCE_TABLE
    union all
    select
        len(remainingString) as currentStringLength,
        cast(try_cast(replace(left(remainingString, 1),' ','z') as tinyint) as nvarchar) as firstCharAsNumeric,
        cast(isNull(newString,'') as nvarchar(3999)) + isNull(cast(try_cast(nullIf(left(remainingString, 1),'') as tinyint) as nvarchar(1)),'') as newString,
        substring(remainingString,2,len(remainingString)) as remainingString
    from FIRST_CHAR_PROCESSED fcp2
    where fcp2.currentStringLength > 1
)
select 
    newString
    ,* -- comment this out when required
from FIRST_CHAR_PROCESSED 
where currentStringLength = 1

那么这里发生了什么?

基本上,在我们的 CTE 中,我们选择第一个字符并使用try_cast参见 docs)将其转换为 a tinyint(对于一位数字来说,这是一个足够大的数据类型)。请注意,SQL Server 中的类型转换规则说空字符串(或空格,就此而言)将解析为零,因此nullif添加 以强制空格和空字符串解析为 null(参见讨论)(否则我们的任何时候在源数据中遇到空格时,结果都将包含一个零字符)。

CTE 还返回第一个字符之后的所有内容——这成为我们对 CTE 的递归调用的输入;换句话说:现在让我们处理下一个字符。

最后,通过串联newString生成 CTE 中的字段(在第二个中)。SELECT对于递归 CTE ,数据类型必须在任何给定列的两个SELECT语句之间匹配 - 包括列大小。因为我们知道我们正在添加(最多)一个字符,所以我们将该字符转换为 nvarchar(1),并将newString(到目前为止)转换为 nvarchar(3999)。连接起来,结果将是 nvarchar(4000) - 它匹配我们在第一个中执行的类型转换SELECT

如果您运行此查询并排除该WHERE子句,您将了解正在发生的事情 - 但行的顺序可能很奇怪。(您不一定会看到与单个输入值相关的所有行组合在一起 - 但您仍然应该能够遵循)。

希望这是一个有趣的选项,可以帮助一些想要严格基于表达式的解决方案的人。

于 2020-11-30T05:56:58.020 回答
-3

如果您正在使用 Postgres 并且您有像“2000 - 一些示例文本”这样的数据,请尝试子字符串和位置组合,否则如果在您的场景中没有分隔符,您需要编写正则表达式:

SUBSTRING(Column_name from 0 for POSITION('-' in column_name) - 1) as 
number_column_name
于 2019-10-10T04:29:47.880 回答