在sql insert语句中添加前导零

时间:2016-08-09 19:48:33

标签: sql db2 ibm-midrange

我有一组字段(所有字符)我从另一个表( old_table )写入表( new_table )。 old_table中有四个字段 - 月,日,世纪,年(再次,在char中)。我将这些日期作为一个字段添加到new_table中 - invdat (char)。客户希望将前导零添加到日期,因为它们当前未存储在 old_table 中。

以下是一个示例:

INSERT INTO new_table 
SELECT month || oedy01 || oecc01 || oeyr01 || as invdat 
FROM old_table

因此,在将这些字段放入new_table中的invdat字段之前,我需要在这些字段中添加前导零。

任何建议表示赞赏。

2 个答案:

答案 0 :(得分:2)

使用旧软件的系统将日期存储为字符串的原因有很多。很可能,因为该软件是在日期数据类型在RPG中轻松获得之前编写的。上面的评论提到它已经用这种方式做了20年。可能有很多代码围绕它并改变它以满足"更新"惯例成本过高。即使DB2 / 400和RPG IV今天处理得非常好,但是谁还想重新编写和重新测试一个大型系统,这个系统在其他工作要做的时候已经好几十年了?也就是说,这是一种使用AS / 400-happy DB2 SQL的简单方法,将您的字段填充到一个日期字段中:

INSERT INTO new_table 
SELECT Right('00'||month,2) || Right('00'||oedy01,2) || Right('00'||oecc01,2) || Right('00'||oeyr01,2) as invdat 
FROM old_table

我建议与AS / 400开发人员一起验证日期格式。通常情况下,您会看到以ccyymmdd格式存储的日期,但对此没有严格的规则。

答案 1 :(得分:2)

按照典型情况,OP没有给出他们的DDL。

FWiW,存储日期的单独组件有很大的好处,如果大多数查询活动依赖于这些组件并且它们被索引;虽然具有派生INDEX的附加功能,但是诸如YEAR(date_field)或SUBSTR(mmddyyyy_string_field,5,4)之类的表达式可以改善运行时派生的问题,以便执行这些选择。以所选格式转换到所描述的INVDAT并不是选择IMO的所有,即使大多数选择不是针对特定的日期组件;我稍后评论。

虽然人们可能期望接受的答案应该给出一般正确的结果,但由于期望数据域是至少一个数字[字符串或数字]的明显日期值,结果完全取决于数据-typing;有意思的是,根据"前导零......当前没有存储"在旧值中,数据类型显着地是字符串与数字,并且每个"(再次,在char中)"人们可能会期待CHAR与VARCHAR。并且作为字符串数据,没有提到非零填充\前置存储值是否可以左调整或右调整。

那个和其他\未来的答案和评论可能不适合实际和未知的\ 未说明的 DDL,因此这些答案应该以有效的"作为一个有效的" 给定&#34 ;;然而,根据典型情况,响应者很少会提供澄清: - (

例如,已经在评论中提到的Left Pad(LPAD)标量[自IBM DB2 for i 7.1以来可用]。虽然LPAD对于预期效果可能是更明显合适的标量请求,而不是使用存在时间更长且功能更强的RIGHT标量[当与一个或多个零字符串的前置组合时,根据实际数据的要求,在OP中也留下未描述,但只有当连接到剥离的值时才确定有效,LPAD可能也会因OP而失败,因为它们的原始数据很可能CHAR [per"(再次,在char中)"]而不是VARCHAR;即LPAD未通过常识测试,未能实现最可能的\理性意图,而是仅作为针对固定长度CHAR数据的有效子串操作,尽管SUBSTR功能已经长期以来存在这种效果。

当然,关于OP对DDL有什么相同的推论,即使表达式RIGHT('00' CONCAT char_2_field, 2)也无法产生理想的结果;例如如果字段中的左侧调整后的值为'1 ',则该表达式实际上要求RIGHT(CHAR('001 ', 4), 2),其结果为'1 ',而不是假定为期望的'01'并且在字段中使用右侧调整后的值' 1',该表达式实际上要求结果为RIGHT(CHAR('00 1', 4), 2)的{​​{1}}而不是假定为期望' 1' }。实际上有点惊讶答案被接受了;也许我感到震惊的是,显然原来的DDL的字段被宣布为VARCHAR,并且这些值显然也在存放或正确施放时被正确地剥离了。

如果这些字段是VARCHAR或由STRIP [或LTRIM或类似]执行以影响不同字符串的数据类型[似乎可选地根据文档对LPAD影响的含义进行左边修剪结果,但我的经验表明建议使用左侧修剪或完全剥离],然后可以按照预期生成字符串,用于答案注释中提供的表达式。

注释中注明的DATE标量可以省略,日期组件的顺序可以重新排序。但是作为字符串而不是DATE数据类型的结果,OP 中显示的格式为MMDDYYYY的字符串不是 井井有条 [缺乏词典质量]所以没用排序和选择除[不]相等谓词以外的任何东西;格式为YYYYMMDD的字符串[几乎与评论试图提供的内容] 井井有条,因此非常适合排序和相对[而不仅仅是匹配|非匹配]选择性;没有对DATE算法和验证的固有支持是一个较小的否定。

注意:以下针对DDL和填充数据的脚本操作假定" cc"是指简单的计数世纪数字两位数值;即" cc"忽视一个零世纪,所以' 19'意味着1900年代而不是19世纪,并且" cc"不是替代映射,例如' - ' 19' 01' - ' 20'等等。

启用给定和备用查询测试的设置:

'01'

现在查询上面设置的内容:

create table old_table
( month    char(2) not null with default
, oedy01   char(2) not null with default
, oecc01   char(2) not null with default
, oeyr01   char(2) not null with default
)
;
create table new_table
( invdat   char(8) not null with default
)
;
insert into old_table /* mm, dd, cc, yy */ values          
  ( ' 1', ' 1', '19', '40' ),  ( '3 ', '3 ', '19', '40' )  
, ( ' 1', ' 1', '20', ' 0' ),  ( '3 ', '3 ', '20', '0 ' )  
, ( ' 2', ' 2', '20', '  ' ),  ( '4 ', '4 ', '20', '  ' )  
, ( ' 1', ' 1', '20', ' 1' ),  ( '3 ', '3 ', '20', '1 ' )  
, ( ' 1', ' 1', '20', '39' ),  ( '3 ', '3 ', '20', '39' )  
, ( ' 1', ' 1', ' 0', ' 1' ),  ( '3 ', '3 ', '0 ', '1 ' )  
, ( ' 2', ' 2', '  ', ' 1' ),  ( '4 ', '4 ', '  ', '1 ' )  
; -- mm(1,2):right-adj, mm(3,4):left-adj, mm(2,4):blank-elems
  -- 14 rows inserted in OLD_TABLE

因此,对于给定的设置,上述查询不是很有帮助。通过对每个查询进行少量修订以确保CHAR数据首先被剥离出多余的空白,结果要好得多:

SELECT Right('00'||month,  2) || Right('00'||oedy01, 2)          
    || Right('00'||oecc01, 2) || Right('00'||oeyr01, 2) as invdat
from old_table
where month not in (2, 4)
; -- report of above query follows; no leading zeroes anywhere:
  INVDAT  
   1 11940
  3 3 1940
   1 120 0
  3 3 200 
   1 120 1
  3 3 201 
   1 12039
  3 3 2039
   1 1 0 1
  3 3 0 1 
-- end of data --
select                                                         
  lpad( month  , 2, '0') concat lpad( oedy01, 2, '0') concat   
  lpad( oecc01 , 2, '0') concat lpad( oeyr01, 2, '0') as invdat
from old_table                                                 
where month not in (2, 4)
; -- report of above query follows; no leading zeroes anywhere:
  INVDAT  
   1 11940
  3 3 1940
   1 120 0
  3 3 200 
   1 120 1
  3 3 201 
   1 12039
  3 3 2039
   1 1 0 1
  3 3 0 1 
-- end of data; a duplicate of the prior query report --

如果DDL是数字并且已知为值存储了有效的十进制数据,则以下可能是代表性脚本:

select                    
  lpad( strip(   month ) , 2, '0') concat   
  lpad( strip(   oedy01) , 2, '0') concat   
  lpad( strip(   oecc01) , 2, '0') concat   
  lpad( strip(   oeyr01) , 2, '0') as invdat
from old_table                              
/* where month not in (2, 4) -- this *fixes* blank values too */
; -- report of above query follows; expected leading zeroes appear:
  INVDAT  
  01011940
  03031940
  01012000
  03032000
  02022000
  04042000
  01012001
  03032001
  01012039
  03032039
  01010001
  03030001
  02020001
  04040001
-- end of data --
SELECT                                             
    Right('00' concat strip( month ) , 2) concat   
    Right('00' concat strip( oedy01) , 2) concat   
    Right('00' concat strip( oecc01) , 2) concat   
    Right('00' concat strip( oeyr01) , 2) as invdat
from old_table                                     
/* where month not in (2, 4) -- this *fixes* blank values too */
; -- report of above query matches the prior; not repeated here

两个查询都提供了[我在上面的示例中包含的变体;带有RIGHT()表达式的表达式和带有答案的LPAD()]的表达式和注释中的表达式将提供与上面相同的报告。这是因为从CHAR到DECIMAL的隐式转换的结果是数值的左调整变化字符串表示,因此隐式左调整和变长,因此这些标量函数将产生所需的输出。

如果以上脚本使用数字数据类型[而不是提及"所有字符"数据类型]是OP选择的回答可接受的原因,然后用于强调问题情景解释得有多么糟糕,并暗示如何通过包括DDL;更好的是,还要包含样本数据。