SQL Server 2008先前字符串提取

时间:2012-03-19 01:44:29

标签: sql sql-server sql-server-2008 tsql

我在SQL列中有类似下面的字符串。我想将它们作为技术总量提取出来。例如:

Original Column ---------> Expected Output from a TSQL function
-------------------------------------------
$15 / 1GB 24m + Intern 120MB ----------> 1.12 GB
$19.95 / 500MB + $49.95 / 9GB Blackberry -----> 9.5GB
$174.95 Blackberry 24GB + $10 / 1GB Datapack ----> 25GB
$79 / 6GB --> 6GB
Null --> Null
$20 Plan --> 0GB

注意:出于我们的目的,1000MB = 1 GB(不是1024)。

模式是数字后跟GB / MB,通常它们组合为1GB(没有任何空格,但有时可能包含空格,如果难以实现此异常,则不是特别重要)。

有时在同一个字符串中最多有三个或四个GB / MB实例,这些实例通常用+号分隔(参见上例中的第2行和第3行)。

我已经看到我们如何在其中一个答案中提取美元值,其中数字后跟$或提取字符串中的所有整数但我不想提取字符串中的美元值或所有整数。我只想要字符串中GB / MB的总和。

1 个答案:

答案 0 :(得分:2)

以下内容可能看起来有些具体而且假设,即使对于特定的和过度假设的解决方案而言,它看起来也可能看起来有点过于复杂。不过,我希望它至少能成为一个良好的起点。

这些是我必须做的假设,以避免进一步使脚本复杂化:

  1. 要提取的值永远不会包含小数点(是整数)。

  2. 要提取的值始终以空格开头或位于列值的开头。

  3. 除了流量大小(要提取的值)之外,GBMB都不可能是其中任何内容的一部分。

  4. GBMB之前都没有空格。

  5. 所有字符串都是唯一的,或者带有可用作键值的另一列或多列。 (我的解决方案特别使用附加列作为键。)

  6. 所以,这是我的尝试(确实返回了原帖中提供的所有样本数据的预期结果):

    WITH data (id, str) AS (
                 SELECT 1, '$15 / 1GB 24m + Intern 120MB' ----------> 1.12 GB
      UNION ALL  SELECT 2, '$19.95 / 500MB + $49.95 / 9GB Blackberry' -----> 9.5GB
      UNION ALL  SELECT 3, '$174.95 Blackberry 24GB + $10 / 1GB Datapack' ----> 25GB
      UNION ALL  SELECT 4, '$79 / 6GB' --> 6GB
      UNION ALL  SELECT 5, Null --> Null
      UNION ALL  SELECT 6, '$20 Plan' --> 0GB
      UNION ALL  SELECT 7, '460MB' --> 0.46GB
    ),
    unified AS (
      SELECT
        id,
        oldstr = str,
        str = REPLACE(str, 'GB', '000MB')
      FROM data
    ),
    split AS (
      SELECT
        id,
        ofs    = 0,
        endpos = CHARINDEX('MB', str),
        length = ISNULL(CHARINDEX(' ', REVERSE(SUBSTRING(str, 1, NULLIF(CHARINDEX('MB', str), 0) - 1)) + ' ') - 1, 0),
        str    = SUBSTRING(str, NULLIF(CHARINDEX('MB', str), 0) + 2, 999999)
      FROM unified
      UNION ALL
      SELECT
        id,
        ofs    = NULLIF(endpos, 0) + 1,
        endpos = CHARINDEX('MB', str),
        length = ISNULL(CHARINDEX(' ', REVERSE(SUBSTRING(str, 1, NULLIF(CHARINDEX('MB', str), 0) - 1)) + ' ') - 1, 0),
        str    = SUBSTRING(str, NULLIF(CHARINDEX('MB', str), 0) + 2, 999999)
      FROM split
      WHERE length > 0
    ),
    extracted AS (
      SELECT
        d.id,
        str = d.oldstr,
        mb = CAST(SUBSTRING(d.str, s.ofs + s.endpos - s.length, s.length) AS int)
      FROM unified d
      INNER JOIN split s ON d.id = s.id
    )
    SELECT
      id,
      str,
      gb = RTRIM(CAST(SUM(mb) AS float) / 1000) + 'GB'
    FROM extracted
    GROUP BY id, str
    ORDER BY id
    

    基本上,这个想法是首先将所有千兆字节转换为兆字节,然后才能搜索并提取仅兆字节数量。搜索& extract方法涉及递归CTE,主要包括以下步骤:

    1)找到第一个MB;

    的位置

    2)在MB;

    之前找到数字的长度

    3)在第一个MB;

    的末尾切断字符串的开头

    4)从步骤1开始重复,直到找不到MB;

    5)将找到的数字加入到原始字符串列表中以自行提取数量。

    之后,我们仍然只能按键值进行分组并对获得的金额求和。这是输出:

    id  str                                           gb
    --  --------------------------------------------  ------
    1   $15 / 1GB 24m + Intern 120MB                  1.12GB
    2   $19.95 / 500MB + $49.95 / 9GB Blackberry      9.5GB
    3   $174.95 Blackberry 24GB + $10 / 1GB Datapack  25GB
    4   $79 / 6GB                                     6GB
    5   NULL                                          NULL
    6   $20 Plan                                      0GB
    7   460MB                                         0.46GB