2012-03-19 29 views
0

我有像下面的SQL列中的字符串。我想将它们提取为总计的千兆字节量。例如:SQL Server 2008之前的字符串提取

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

你有什么试过?因为你需要使用'SUBSTRING'来设置东西。 – 2012-03-19 01:50:01

+0

@OMGPonies我不知道该怎么做,说实话,有点困惑。我试图使用CTE递归,然后意识到如何从字符串中回到前端,因为GB或MB最终是如此令人困惑。 – 2012-03-19 02:13:30

+0

我不羡慕你 - 字符串处理将变得脆弱。任何可能性都需要在逻辑中加以解释。 CTE过于复杂。 – 2012-03-19 02:14:55

回答

2

下可能会出现一定程度的具体和太假设,即使它也可能看起来有点太复杂,具体和过度承担解决方案。不过,我希望它至少能够起到一个好的起点。

这是我不得不做,以避免脚本甚至更加复杂的假设:

  1. 将永远提取的值包含小数点(都是整数)。

  2. 要提取的值总是在空格之前或在列值的开始处。

  3. GBMB都不是流量大小(要提取的值)的其他部分。

  4. GBMB之前都没有空格。

  5. 所有的字符串都是唯一的,或者伴随着可以用作键值的另一列或多列。 (我的解决方案,特别是使用一个额外的列作为一个关键。)

所以,这里是我的企图(这并返回原岗位提供的所有样本数据的预期结果):

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 

基本上,这个想法是首先将所有千兆字节转换为兆字节,然后才能够搜索并提取仅兆字节的金额。搜索&提取方法涉及递归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 
+0

非常慷慨。非常感谢你! – 2012-03-20 04:16:27

相关问题