将格式应用于表中的所有行的过程

时间:2016-08-31 19:33:50

标签: sql postgresql plpgsql

我有一个SQL程序,它在每一行中递增,并在值上填充一些尾随零,具体取决于小数点后的值的长度。试图将其转移到PSQL环境中我意识到SQL和PSQL之间存在很多语法差异。我设法随着时间的推移进行转换,但我仍然遇到语法错误,无法弄清楚原因。有人可以帮我弄清楚为什么这不会运行?我目前在PGadmin中运行它,如果这有任何区别。

DO $$

DECLARE 
counter integer;
before decimal;  
after decimal;

BEGIN
counter := 1;
WHILE counter <> 2 LOOP 


    before = (select code from table where ID = counter);  

    after = (SELECT SUBSTRING(code, CHARINDEX('.', code) + 1, LEN(code)) as Afterward from table where ID = counter);   

    IF before = after  
    THEN
    update table set code = before + '.0000' where ID = counter;  

    ELSE
        IF length(after) = 1 THEN
        update table set code = before + '000' where ID = counter;
        ELSE IF length(after) = 2 THEN
        update table set code = before + '00' where ID = counter;
        ELSE IF length(after) = 3 THEN
        update table set code = before + '0' where ID = counter;
        ELSE
        select before;
        END IF;
    END IF;

counter := counter + 1;

END LOOP
END $$;

预期结果的输入/输出的一些示例:

Input 55.5 >  Output  55.5000
Input 55   >  Output 55.0000

感谢您的帮助, 贾斯汀

2 个答案:

答案 0 :(得分:0)

显示时,表格上不需要函数甚至更新来格式化值。

假设这些值实际上是存储在decimalfloat列中的数字,您需要做的就是在检索时应用to_char()函数:

select to_char(code, 'FM999999990.0000')
from data;

这将输出55.5000或55.0000

to_char()函数的缺点是您需要预测可能发生的最大位数。如果格式掩码中的9不够,则输出将类似于#.###。但由于格式掩码中的数字太多而没有受到伤害,我通常会在格式掩码中投入很多。

有关格式化功能的详细信息,请参阅手册:
https://www.postgresql.org/docs/current/static/functions-formatting.html#FUNCTIONS-FORMATTING-NUMERIC-TABLE

如果您坚持存储格式化数据,可以使用to_char()更新表格:

update the_table
  set code = to_char(code::numeric, 'FM999999990.0000');

如果列中存在非数字值,则将值转换为数字当然会失败。

但同样:我强烈建议将数字存储为数字,而不是字符串。

如果要将其与用户输入进行比较,最好将用户输入转换为正确的数字,并将 与存储在数据库中的(数字)值进行比较。

您所追求的字符串匹配实际上并不需要函数。将substring()与正则表达式一起使用即可:

update the_table 
   set code = code || case length(coalesce(substring(code from '\.[0-9]*$'), ''))
         when 4 then '0'
         when 3 then '00'
         when 2 then '000'
         when 1 then '0000'
         when 0 then '.0000'
         else ''
       end
 where length(coalesce(substring(code from '\.[0-9]*$'), '')) < 5;

substring(code from '\.[0-9]*$')提取.后面跟着字符串末尾的数字的所有内容。因此,对于55.0,它会为.0返回55.50,如果值中没有.50,则返回.,然后返回null这就是为什么需要合并。

该子字符串的长度告诉我们存在多少位数。根据我们的不同,我们可以附加必要数量的零。可以缩短case,以便不必列出所有可能的长度(但这并不简单):

update the_table
    set code = code || case length(coalesce(substring(code from '\.[0-9]*$'), ''))
         when 0 then '.0000'
         else lpad('0', 5- length(coalesce(substring(code from '\.[0-9]*$'), '')), '0')
       end
 where length(coalesce(substring(code from '\.[0-9]*$'), '')) < 5;

另一种选择是使用字符串中.的位置来计算需要添加的0的数量:

update the_table
     set code = 
       code || case 
         when strpos(code, '.') = 0 then '0000'
         else rpad('0', 4 - (length(code) - strpos(code, '.')), '0')
       end
where length(code) - strpos(code, '.') < 4;

正则表达式相当昂贵,不使用它们会使速度更快。但是,只有在值中最多只有一个.时,上述内容才有效。

但是,如果您可以确定每个值都可以转换为数字,那么带有强制转换的to_char()方法肯定是最强大的。

要仅处理code列包含正确数字的行,可以在SQL语句中使用where子句:

where code ~ '^[0-9]+(\.[0-9][0-9]?)?$'

答案 1 :(得分:0)

要将列类型更改为数字:

alter table t alter column code type numeric