我有一组字段(所有字符)我从另一个表( 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字段之前,我需要在这些字段中添加前导零。
任何建议表示赞赏。
答案 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;更好的是,还要包含样本数据。