试图从字符串中获取所有数值和句点
Posted
技术标签:
【中文标题】试图从字符串中获取所有数值和句点【英文标题】:Trying to get all numerical values and periods form string 【发布时间】:2019-04-18 15:18:48 【问题描述】:我正在尝试使用 SELECT 语句检索数据,该语句由不同的人输入,以克为单位。由于似乎没有标准,这里有一些人们编写它的方式:
200克 0.2KG 0.200公斤 0.2 0.2g(e) 2KG .222我目前在下面有这个函数,它将所有数字字符返回到 NVARCHAR 中,然后我可以将其转换为小数以用于计算。
ALTER FUNCTION [dbo].[fn_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
这在某些情况下确实可以正常工作,但例如,任何写成“0.2...”的数据都会变成“2”,这在使用克和千克时没有帮助。
我需要它输出到 DECIMAL 或 NVARCHAR 并保留小数位位置,因此 0.2 保持为 0.2 而 0.200 保持不变。
(字母只会出现在数字的开头或结尾,不会出现在数字之间,有时会以.
开头)
【问题讨论】:
您真的还在使用 SQL Server 2005 吗?现在已经不在支持范围内了。然而,更好的问题是,为什么要将数字数据存储为varchar
而不是数字数据类型(即使您的函数也返回 varchar
)。
我相信它很快就会转移到 2012,它根本不是我们经常使用的服务器,但它是一个旧的,哈哈!因为当它最初存储时,它只是使用的格式,不幸的是我没有任何方法可以改变它。它返回 varchar 因为这是最后的数据类型,就像我说的那样,它根本不会像 varchar 一样产生任何麻烦,这不是问题。
但为什么它最初存储为varchar
?正如您所发现的,错误的数据类型选择总是一个问题。不幸的是,我能想到的任何解决方案都只能在 SQL Server 2008+ 上运行。就像我说的,2005 现在已经不再支持了,所以任何升级都应该已经完成了。为什么你也只去2012年?这也不受支持(仅在扩展中)。目前仅完全支持 SQl Server 2014 - 2017。
因为最初我们不需要处理数字。我相信这是一个只有文本字段的电子表格,所以当人们输入一个数字时,这取决于用户对他们如何编写它的偏好。不要误会我的意思,我知道这根本不是一个好方法,但这是我必须使用的,我不能彻底手动更改它们然后更改数据类型,还有很多方法数据来做到这一点。我认为它尚未完全决定我们将为该特定服务器迁移到哪个版本,这不是我参与的决定。
@A.Cassin 你可能不应该迁移到 2012 年,因为它已经脱离了主流支持。安装旧版本并不意味着您可以避免错误。这意味着您获得了所有在以后的版本中修复的错误。最终也需要更昂贵的许可证。 Since 2016 SP1 features 仅在企业版中可用,甚至在 Express 中也可用,包括压缩、内存表、分区等
【参考方案1】:
你可以找到一些没有使用的字符,并用它替换.0
。然后放回去:
BEGIN
DECLARE @intAlpha INT
SET @strAlphaNumeric = REPLACE(@strAlphaNumeric, '.0', '#');
SET @strAlphaNumeric = REPLACE(@strAlphaNumeric, '0.', '##');
SET @strAlphaNumeric = REPLACE(@strAlphaNumeric, '.', '###');
SET @intAlpha = PATINDEX('%[^0-9#]%', @strAlphaNumeric)
WHILE @intAlpha > 0
BEGIN
SET @strAlphaNumeric = STUFF(@strAlphaNumeric, @intAlpha, 1, '' )
SET @intAlpha = PATINDEX('%[^0-9#]%', @strAlphaNumeric )
END
SET @strAlphaNumeric = REPLACE(@strAlphaNumeric, '###', '.')
SET @strAlphaNumeric = REPLACE(@strAlphaNumeric, '##', '0.');
SET @strAlphaNumeric = REPLACE(@strAlphaNumeric, '#', '.0');
SELECT ISNULL(@strAlphaNumeric,0)
END
或者像这样添加.
:
PATINDEX('%[^0-9.]%', @strAlphaNumeric)
但如果你有这样的字符串:te....st 5.0 kg
,你会遇到问题,所以最好用特殊的东西替换.0
。
注意,在示例中,我将替换为 #
,但您可以替换为其中的五个以使值更加独特 - 例如,#####
。
【讨论】:
【参考方案2】:T-SQL 中的字符串解析总是具有挑战性。但在这种情况下,SUBSTRING 可能会更容易。
您提到了这一点:字母只在开头或结尾,从不在数字之间
所以基本上我们只需要找到数字在字符串中的开始和结束位置。
看看这个:
DECLARE @String NVARCHAR(100)
SET @String = 'This is k.g. or g it''s .0456 not g'
SELECT PATINDEX('%[0-9 ].%[0-9]%', @String ) --Where is the first occurance of a number
SELECT PATINDEX('%[0-9]%', REVERSE(@String)) --Flip the string and tell me from the other end where that number occurs
SELECT LEN(@String) - PATINDEX('%[0-9 ].%[0-9]%', REVERSE(@String)) - PATINDEX('%[0-9]%', @String ) +2 --substract those from the length and add 2, tells me how long it is.
--The above is basically where the number starts and it's length in the string.
--Then use substring to pull it all out.
SELECT SUBSTRING(@String, PATINDEX('%[0-9 ].%[0-9]%', @String ), LEN(@String) - PATINDEX('%[0-9]%', REVERSE(@String)) - PATINDEX('%[0-9 ].%[0-9]%', @String ) +2)
--larger sample set
IF OBJECT_ID('tempdb..#TestData') IS NOT NULL
DROP TABLE #TestData
CREATE TABLE #TestData (
NumberData NVARCHAR(100)
)
INSERT INTO #TestData (
[NumberData]
)
VALUES
('200g')
,('0.2KG')
,('0.200kg')
,('0.2')
,('0.2g(e)')
,('0.2000000k.g.')
,('grams 0.345')
,('This is k.g. 0.456 not g')
,('.456kg')
SELECT *, SUBSTRING([NumberData], PATINDEX('%[0-9 ].%[0-9]%', [NumberData] ), LEN([NumberData]) - PATINDEX('%[0-9]%', REVERSE([NumberData])) - PATINDEX('%[0-9 ].%[0-9]%', [NumberData] ) +2)FROM #TestData
【讨论】:
不完全确定,但运行它有问题。说,('0.2KG')
位置的语法不正确
@A.Cassin 抱歉,我没看到。我刚刚复制并粘贴了我的示例并运行它没有错误。
@A.Cassin 哦等等,我的错,你是 2005 年,这个例子使用了一个表变量,它直到 2008 年才出现。我会更新,所以有一个工作示例。以上是关于试图从字符串中获取所有数值和句点的主要内容,如果未能解决你的问题,请参考以下文章