查询仅从字符串中获取数字

时间:2013-05-21 10:00:54

标签: sql sql-server

假设我有这样的数据:

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

我期望的输出是

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

我想在sql中实现它。 请帮忙。 在此先感谢:)

19 个答案:

答案 0 :(得分:85)

首先创建 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

现在使用 function 作为

SELECT dbo.udf_GetNumeric(column_name) 
from table_name

<强> SQL FIDDLE

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

<强> Reference

答案 1 :(得分:30)

试试这个 -

<强>查询:

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

答案 2 :(得分:10)

查询:

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

答案 3 :(得分:8)

请尝试:

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

答案 4 :(得分:3)

通过之前的查询,我得到了以下结果:

'AAAA1234BBBB3333'&gt;&gt;&gt;&gt;输出:1234

' - çã+ 0!\aº1234'&gt;&gt;&gt;&gt;输出: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

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

然后在相同的数据上测试来自 top voted answer 的 UDF。

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

Inspiration for 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

答案 6 :(得分:1)

首先找出数字的起始长度,然后反转字符串以再次找出第一个位置(这将为您提供数字的结束位置)。现在,如果您从两个数字中减去 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))

答案 7 :(得分:1)

此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

答案 8 :(得分:1)

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

答案 9 :(得分:0)

我没有创建函数的权限,但拥有类似

的文字
["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

答案 10 :(得分:0)

只需对@Epsicron的答案进行一些修改

.mat-form-field-label {
 white-space: pre-wrap;
}

不需要临时变量

答案 11 :(得分:0)

尽管这是一个古老的线程,它是google搜索中的第一个线程,但我想出了一个不同于以前的答案。这将使您可以通过准则来决定字符串中要保留的内容,无论该准则可能是什么。如果需要,可以将其放在函数中以反复调用。

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)

答案 12 :(得分:0)

在Oracle中

您可以使用以下方法获得想要的东西:

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

答案 13 :(得分:0)

考虑到其他答案,我想出了自己的答案: 试试这个:

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

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

答案 14 :(得分:0)

SELECT regexp_replace(column_name,'[^ 0-9] *','','g');

答案 15 :(得分: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;

答案 16 :(得分:0)

为了它的地狱...

此解决方案与所有早期解决方案不同,即:

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

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

还请注意-此解决方案使用recursive common table expression(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_castsee docs)将其转换为tinyint(这是一个足够大的数据类型,可以容纳一位数字)数字)。请注意,SQL Server中的类型转换规则说空字符串(或空格)将解析为零,因此添加了nullif来强制空格和空字符串解析为null({ {3}})(否则,只要在源数据中遇到空格,我们的结果将包括一个零字符)。

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

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

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

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

答案 17 :(得分:-2)

如果您使用的是Postgres,并且有类似“ 2000-一些示例文本”的数据,请尝试使用子字符串和位置组合,否则,如果在您的方案中没有定界符,则需要编写正则表达式:

SUBSTRING(Column_name from 0 for POSITION('-' in column_name) - 1) as 
number_column_name

答案 18 :(得分:-3)

在查询中,您可以替换任何字符,它不是数字,没有任何内容:

REPLACE( ISNULL( column_name, '' ), '[^0-9]', '' )