【问题标题】:Extract float from String/Text SQL Server从字符串/文本 SQL Server 中提取浮点数
【发布时间】:2013-11-21 09:18:29
【问题描述】:

我有一个应该具有浮动值(价格)的数据字段,但是数据库设计人员搞砸了,现在我必须在该字段上执行聚合函数。而 80% 的时间数据格式正确,例如。 “80.50”,有时会保存为“80.50 美元”或“每平方米 80.50 美元”。

数据字段是 nvarchar。我需要做的是从 nvarchar 中提取浮点数。我遇到了这个:Article on SQL Authority

然而,有些人可能会说,这解决了我一半的问题,或者使问题复杂化。该函数只返回字符串中的数字。也就是说,“每平方米 80.50 美元”将返回 80502。显然这不起作用。我试图从 => 更改正则表达式 PATINDEX('%[^0-9]%', @strAlphaNumeric) to=> PATINDEX('%[^0-9].[^0-9]%', @strAlphaNumeric) 不工作。任何帮助将不胜感激。

【问题讨论】:

  • 您对“每平方米 80.50 美元”的回答有何期望??

标签: sql sql-server regex


【解决方案1】:

这确实需要你需要,在 (http://sqlfiddle.com/#!6/6ef8e/53) 上测试

DECLARE @data varchar(max) = '$70.23 per m2'
Select LEFT(SubString(@data, PatIndex('%[0-9.-]%', @data), 
                  len(@data) - PatIndex('%[0-9.-]%', @data) +1
                 ), 
        PatIndex('%[^0-9.-]%', SubString(@data, PatIndex('%[0-9.-]%', @data), 
                  len(@data) - PatIndex('%[0-9.-]%', @data) +1))
        )

但是正如 jpw 已经提到的那样,在 CLR 上使用正则表达式会更好

【讨论】:

  • 这将丢失像80.50$80.50 这样的值并返回空白。
  • 我用你的值对其进行了测试,但找不到除 123. 55 之外的值,但 RegEx 也会失败
【解决方案2】:

这也应该有效,但它假定浮点数后跟一个空格,以防后面有文本。

// sample data
DECLARE @tab TABLE (strAlphaNumeric NVARCHAR(30))
INSERT @tab VALUES ('80.50'),('$80.50'),('$80.50 per sqm')

// actual query
SELECT 
  strAlphaNumeric AS Original, 
  CAST (
    SUBSTRING(stralphanumeric, PATINDEX('%[0-9]%', strAlphaNumeric), 
      CASE WHEN PATINDEX('%[ ]%', strAlphaNumeric) = 0 
      THEN LEN(stralphanumeric) 
      ELSE 
      PATINDEX('%[ ]%', strAlphaNumeric) - PATINDEX('%[0-9]%', strAlphaNumeric)
      END
    ) 
    AS FLOAT) AS CastToFloat
FROM @tab

从上面它生成的示例数据:

Original                       CastToFloat
------------------------------ ----------------------
80.50                          80,5
$80.50                         80,5
$80.50 per sqm                 80,5

示例SQL Fiddle

如果您想要更健壮的东西,您可能需要考虑编写一个 CLR 函数来进行正则表达式解析,而不是像这篇 MSDN 文章中描述的那样:Regular Expressions Make Pattern Matching And Data Extraction Easier

【讨论】:

    【解决方案3】:

    受到@deterministicFail 的启发,我想到了一种仅提取数字部分的方法(尽管还不是 100%):

    DECLARE @NUMBERS TABLE (
        Val VARCHAR(20)
    )
    INSERT INTO @NUMBERS VALUES
    ('$70.23 per m2'),
    ('$81.23'),
    ('181.93 per m2'),
    ('1211.21'),
    (' There are 4 tokens'),
    ('  No numbers    '),
    (''),
    ('  ')
    select
        CASE
            WHEN ISNUMERIC(RTRIM(LEFT(RIGHT(RTRIM(LTRIM(n.Val)), 1+LEN(RTRIM(LTRIM(n.Val)))-PatIndex('%[0-9.-]%', RTRIM(LTRIM(n.Val)))), LEN(RIGHT(RTRIM(LTRIM(n.Val)), 1+LEN(RTRIM(LTRIM(n.Val)))-PatIndex('%[0-9.-]%', RTRIM(LTRIM(n.Val)))))- PATINDEX('%[^0-9.-]%',RIGHT(RTRIM(LTRIM(n.Val)), 1+LEN(RTRIM(LTRIM(n.Val)))-PatIndex('%[0-9.-]%', RTRIM(LTRIM(n.Val))))))))=1 THEN
                RTRIM(LEFT(RIGHT(RTRIM(LTRIM(n.Val)), 1+LEN(RTRIM(LTRIM(n.Val)))-PatIndex('%[0-9.-]%', RTRIM(LTRIM(n.Val)))), LEN(RIGHT(RTRIM(LTRIM(n.Val)), 1+LEN(RTRIM(LTRIM(n.Val)))-PatIndex('%[0-9.-]%', RTRIM(LTRIM(n.Val)))))- PATINDEX('%[^0-9.-]%',RIGHT(RTRIM(LTRIM(n.Val)), 1+LEN(RTRIM(LTRIM(n.Val)))-PatIndex('%[0-9.-]%', RTRIM(LTRIM(n.Val)))))))
            ELSE '0.0'
        END
    FROM @NUMBERS n
    

    【讨论】:

    • 产生不错的输出,但亲爱的上帝到那里很乱。 46 x LEFT/RIGHT/LTRIM/RTRIM
    • @OGHaza,我同意。根据输入字符串,您可以删除 ltrim 和 rtrim。当我插入空白字符串''作为测试用例时,我已经使用了这些函数。
    • 好点。如果有前导空格,jpw 的答案就会失败 - 当然他也可以修剪输入。
    猜你喜欢
    • 1970-01-01
    • 2020-08-04
    • 2019-08-06
    • 2022-09-23
    • 2020-09-29
    • 1970-01-01
    • 2010-10-30
    • 1970-01-01
    • 2021-12-08
    相关资源
    最近更新 更多