我有这样的数据:
string 1: 003Preliminary Examination Plan
string 2: Coordination005
string 3: Balance1000sheet
我期望的输出是
string 1: 003
string 2: 005
string 3: 1000
我想在 SQL 中实现它。
我有这样的数据:
string 1: 003Preliminary Examination Plan
string 2: Coordination005
string 3: Balance1000sheet
我期望的输出是
string 1: 003
string 2: 005
string 3: 1000
我想在 SQL 中实现它。
首先创建这个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
as
SELECT dbo.udf_GetNumeric(column_name)
from table_name
我希望这能解决你的问题。
试试这个——
询问:
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
询问:
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
请试试:
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
仅从字符串中获取数字可以在单行中完成。尝试这个 :
SUBSTRING('your-string-here', PATINDEX('%[0-9]%', 'your-string-here'), LEN('your-string-here'))
注意:仅适用于字符串中的第一个 int,例如:abc123vfg34 返回 123。
通过前面的查询,我得到了这些结果:
'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
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
此 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
我没有创建功能的权利,但有类似的文字
["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
虽然这是一个旧线程,它是谷歌搜索中的第一个,但我想出了一个与以前不同的答案。这将允许您通过您的标准来保存字符串中的内容,无论该标准可能是什么。如果你愿意,你可以把它放在一个函数中一遍又一遍地调用。
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)
对@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)
不需要临时变量
我发现这种方法的运行速度比投票率最高的答案快 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
小数点
如果您需要它来处理小数,您可以使用以下任何一种方法,我发现它们之间没有明显的性能差异。
'[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
这是最简单和最容易的一种。这也适用于多次出现的整个字符串。
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
首先找出数字的起始长度,然后反转字符串以再次找出第一个位置(这将为您提供数字的结束位置)。现在,如果你从两个数字中减去 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))
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;
在甲骨文中
你可以用这个得到你想要的:
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
对于它的地狱...
该解决方案不同于所有早期的解决方案,即:
但首先 - 请注意,该问题并未指定此类字符串的存储位置。在下面的解决方案中,我创建了一个 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
子句,您将了解正在发生的事情 - 但行的顺序可能很奇怪。(您不一定会看到与单个输入值相关的所有行组合在一起 - 但您仍然应该能够遵循)。
希望这是一个有趣的选项,可以帮助一些想要严格基于表达式的解决方案的人。
如果您正在使用 Postgres 并且您有像“2000 - 一些示例文本”这样的数据,请尝试子字符串和位置组合,否则如果在您的场景中没有分隔符,您需要编写正则表达式:
SUBSTRING(Column_name from 0 for POSITION('-' in column_name) - 1) as
number_column_name