我使用以下命令将数据从文本文件复制到红移表中:
从's3://gamma-audit-calculation-output-ngr-data-json/2021/05/10/08/kinesis-calculation-output-ngr-data-1-2021-05-10-09-48-24-82ecea90-ef50-4907-82d7-8b162ca2b841‘凭据redshift_table_name复制iam_role json 'auto';
附加显示在指定路径中的文件。
档案中的数据如下:
{“总薪酬”:6113.47,“总净支付”:3661.6,“计算时间”:“05/10/2021 02:48:24 AM PDT",”动态启动“:真,”雇员“:”881448“,”总提款“:6.62,”累计计算收入“:12.1,”支付日期“:”2021-04-30“,”计算时间划时代“:”1620640104258“,"ngr":0.60,”总时间付款“:0.0,”其他还款“:0.0,“付款日期”:“2021-04-30”,"employeeid_calculationtimeepochmillis":"881448_1620640104258"}
我的红移表的模式是:-
创建表table_name ( employeeid varchar(65535),ngr数值(17,2),总毛数(17,2),总净薪酬数字(17,2),收入数值(17,2),累计收入数字(17,2),任何计时器支取数数值(17,2),总计支付宝数(17,2),动态布尔值,应付日期变量(65535),付款日期变量(65535),计算时间varchar(65535),otherRepayments数字(17,2),计算时历元employeeid_calculationtimeepochmillis varchar(65535);
在这里,我面临的问题是,当保存到Redshift表时,ngr值将更改为0.59而不是0.60。这怎么可能?
发布于 2021-05-10 19:41:01
(
employeeid varchar(65535),
ngr numeric(17, 2),
totalgross numeric(17, 2),
totalnetpay numeric(17, 2),
earningamount numeric(17, 2),
totalimputedincome numeric(17, 2),
totalanytimepaywithdrawals numeric(17, 2),
totalanytimepayrepayments numeric(17, 2),
dynamicngrlaunched boolean,
paycheckdate varchar(65535),
payenddate varchar(65535),
calculationtime varchar(65535),
otherRepayments numeric(17, 2),
calculationtimeepochmillis bigint,
employeeid_calculationtimeepochmillis varchar(65535)
)
DISTKEY (employeeid)
SORTKEY (calculationtimeepochmillis);在讨论其他内容之前,我建议您在最强烈的条件下不要使用最大长度varchar。最后我知道,当行被带入内存时,它们使用的内存量等于它们的最大长度,如DDL中所指定的。您有五个varchar(65535),所以表的一行使用320 so的内存。
请记住,可用内存被划分为队列和插槽,然后跨片,因此您可能真的没有太多可用内存--内存可能有很大的变化,但总共可能大约是100 or -如果要进行散列连接,则需要确保哈希连接中的较小的表在散列放入内存时可以使用,否则性能会变差。如果您有一个正在运行的查询,它将需要用于其他事情的内存,因此,如果您确实有100 of,那么您最多可以说您的哈希有一半可用,而当您有320 of行时,50 of将给您提供表中最多150行的内存。当然,你可以通过这个-红移不会阻止你,它不会警告你任何方式-但性能会下地狱,你将不知道为什么。
对数字也要小心,不要超过19的精度。当精度为19或更低时,numeric是8个字节,但当20字节或更多时,numeric变成16字节(不管实际存储的值如何),必须由数学库( rathar )来处理,而不是由处理器硬件直接处理。
此外,请记住在可能的情况下使用NOT NULL,因为它减少了列的大小。这对于boolean尤为重要,NOT NULL时为1位,NULL为2位,varchar为varchar,因为NULL为字符串存储的数据的大小增加了一个字节。
最后,您没有设置任何编码。红移会为你选择它们,但它在挑选编码方面做得很糟糕。我强烈建议你选择自己的编码。
现在,谈谈你的问题。
这里面临的问题是,当保存到Redshift表时,ngr值更改为0.59,而不是0.60。这怎么可能?
我可能错了,我需要进行测试来检查,但我可能会猜测这个数字首先被读取为一个浮点数,然后转换为一个数字。
整数(在遮罩下是numeric )和浮点数的行为不同。
整数是精确的。浮点数不是。我的意思是说,当你存储一个整数时,你会得到,总是,准确地存储的数字。浮点数的情况并非如此。如果你把最小和最大的浮点数之间的连续数列想象成一个纠察栅栏,那么你会时不时地看到一个进入地球的柱子,只有柱子上的数字才能被储存,所以当你存储一个数字时,它被转换成最近的可存储数,这就是存储的内容,这就是你要回的东西。
所以当你存储0.60的时候,在0.60没有"post“--最近的是0.59,所以0.59是存储的,这就是当你读数字的时候得到的。
如果你想要精确的数字,你可以把你的数字乘以10的幂,所以小数部分总是零,然后将它们存储为整数。对于0.59的例子,假设你的所有数字都有小数部分的两个小数位,把你的数字乘以100,然后0.59变成59,然后存储59作为一个整数。使用整数做所有的数学运算,最后在最后阶段转换回浮点。
大卫·戈德伯格( David )的一篇著名白皮书“每个计算机科学家都应该知道的关于浮点运算的知识”解释了这个问题;
https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
发布于 2021-11-05 11:14:38
我认为cause 15782476的响应完美地描述了问题的根本原因。它帮助我很大程度上理解了正在发生的事情,因为我正在努力解决同样的问题。不过,我已经找到了解决这个问题的办法,这可能会有帮助。为了解决Redshift正在截断小数位而不是舍入小数的问题,您需要稍微调整值。例如,您希望0.60被持久化,然后将0.601写入数据文件中。这将导致浮动值0.600999而不是0.599999,这将被正确地截断为0.60。下面是一个使用反射来调整所有BigDecimal字段值的Java方法(例如,对于标度= 2的0.60,它将增加0.001)
private <T> T adjustNumericFields(T entity) {
for (Field f : entity.getClass().getDeclaredFields()) {
if (f.getType().equals(BigDecimal.class)) {
try {
f.setAccessible(true);
BigDecimal bigDecimal = (BigDecimal) f.get(entity);
if (bigDecimal != null && bigDecimal.doubleValue() > 0d) {
int scale = bigDecimal.scale();
BigDecimal correction = BigDecimal.ONE.movePointLeft(scale + 1);
f.set(entity, bigDecimal.add(correction));
}
} catch (Exception e) {
log.warn("Could not adjust value: {}/{} - {}", entity.getClass().getSimpleName(), f.getName(), e.getMessage());
}
}
}
return entity;
}https://stackoverflow.com/questions/67470561
复制相似问题