【问题标题】:BigQuery financial calculation - Round issuesBigQuery 财务计算 - 回合问题
【发布时间】:2017-05-23 04:43:52
【问题描述】:

我对 BigQuery 和处理财务计算有一个真正的问题。根据查询,它似乎少了几分钱,这不符合我的需要。这是一个例子。考虑这个非常简单的数据集:

TOT_AMT,DLY_AMT,SUN_AMT,CREDIT_COPIES,UNIT_COST_DLY,UNIT_COST_SUNDAY,DAILY_COPIES,SUNDAY_COPIES 81.91,16.58,65.33,15,1.105,4.355,1,1 10.67,0.0,10.67,3,1.245,3.555,0,1 24.74,8.16,16.58,3,1.36,5.525,2,1 38.03,0.0,38.03,9,0.0,4.225,0,1

现在进行基本的舍入和测试不会太难,但不幸的是。我尝试了多种方法,如下所述。我只能关闭一个查询。这是要运行的查询:

SELECT
  TOT_AMT,
  ROUND(ROUND(UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES *10000)/10000 + ROUND(UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES * 10000)/10000,2) AS TOT_AMT_calc1,
  ROUND( ( ( UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES * 100 ) + ( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES * 100 ) )/100, 2) AS TOT_AMT_calc2,
  ROUND( ( ( UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES ) + ( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES ) ), 2) AS TOT_AMT_calc_FULL,
  ROUND( ( ( UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES *100) + ( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES *100 ) ), 2) AS TOT_AMT_calc_FULL2,
  ( ( UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES ) + ( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES ) ) AS TOT_AMT_calc_FULL_NOROUND,
  ROUND(ROUND( ( UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES *1000) + ( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES *1000) )/1000,2) AS TOT_AMT_calc_thousand,
  ROUND(ROUND( ( UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES *100) + ( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES *100) )/100,2) AS TOT_AMT_calc_works
FROM
  `my_project.my_table`

这个数据集的输出令人震惊:

TOT_AMT,TOT_AMT_calc1,TOT_AMT_calc2,TOT_AMT_calc_FULL,TOT_AMT_calc_FULL2,TOT_AMT_calc_FULL_NOROUND,TOT_AMT_calc_thousand,TOT_AMT_calc_works 81.91,81.9,81.9,81.9,8190.0,81.9,81.9,81.9 10.67,10.66,10.66,10.67,1066.5,10.6650000000000001,10.66,10.67 24.74,24.73,24.73,24.74,2473.5,24.7350000000000003,24.73,24.74 38.03,38.02,38.02,38.02,3802.5,38.025,38.02,38.03

如您所见,正确获得基本财务计算所期望的舍入的唯一方法是使用此功能:

ROUND(ROUND( ( UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES *100) + ( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES *100) )/100,2)

我需要一些东西来充分发挥作用,以确保对财务进行完美的计算。在 BigQuery 问题跟踪器上的这个问题中可以概述我想要超过 100 (10,000) 次的主要愿望: https://issuetracker.google.com/issues/35906014

唉,这也不管用,round 不再正常运行。

任何额外的见解将不胜感激。我需要一种可重复且准确的方法来计算真实的财务数据,而 BigQuery 轮次即使在小数字上也失败了。 UDF 会更好吗?

* 更新 * 通过将表转换为整数并导出表进行了一些额外的测试。首先基本上乘以 100,第二次乘以 10000。BigQuery 似乎将 Integer 存储为具有类似舍入问题的 int64。对于所有整数都乘以 10,000 的表格,我能得到准确结果的唯一方法与 float64 中的方法基本相同。即在浮点值乘以 100 处舍入。

SELECT
  TOT_AMT,
  ROUND((( ( UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES) + ( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES) )/10000 ),2) AS TOT_AMT_calc_fail1,
  ((( ( UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES) + ( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES) )/10000 )) AS TOT_AMT_calc_fail2,
  (ROUND(( ( UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES) + ( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES) )/100 )/100) AS TOT_AMT_calc_works3
FROM
  `my_project.my_table`

我什至尝试重新施法,但没有任何影响。转换回 float 似乎没有任何作用,因为它看起来 INT64 的反应与 FLOAT64 相同。

【问题讨论】:

  • 旁注,即使是 excel 也有舍入问题。尽管有舍入误差,但通过下面的答案,我能够正确获得正确的值。

标签: google-bigquery user-defined-functions udf


【解决方案1】:

在与 Google 团队互动并查找浮点数的舍入误差后,我找到了一个相当准确的解决方案。它允许我将我的值存储为 BigQuery 中的浮点数,但是

SELECT
  TOT_AMT,
  ROUND(ROUND(UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES *10000)/10000 + ROUND(UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES * 10000)/10000,2) AS TOT_AMT_calc1,
  ROUND( ( ( UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES * 100 ) + ( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES * 100 ) )/100, 2) AS TOT_AMT_calc2,
  ROUND( ( ( UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES ) + ( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES ) ), 2) AS TOT_AMT_calc_FULL,
  ROUND( ( ( UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES *100) + ( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES *100 ) ), 2) AS TOT_AMT_calc_FULL2,
  ( ( UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES ) + ( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES ) ) AS TOT_AMT_calc_FULL_NOROUND,
  ROUND(ROUND( ( UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES *1000) + ( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES *1000) )/1000,2) AS TOT_AMT_calc_thousand,
  ROUND(ROUND( ( UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES *100) + ( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES *100) )/100,2) AS TOT_AMT_calc_mostly_works,
  ROUND( ( FLOOR(UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES *1000000000) + FLOOR( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES *1000000000) ) / 1000000000+.005,2) AS TOT_AMT_calc_WORKS
FROM
  `my_project.table`

重要的行在这里:

ROUND( ( FLOOR(UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES *1000000000) + FLOOR( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES *1000000000) ) / 1000000000+.005,2) AS TOT_AMT_calc_WORKS

这消除了浮点错误,将浮点数转换为纳秒,在每次乘法后删除所有垃圾,并给我一个准确的结果。

如果我的结果需要小数点后 3 位,我会将此行更改如下:

ROUND( ( FLOOR(UNIT_COST_DLY * DAILY_COPIES * CREDIT_COPIES *1000000000) + FLOOR( UNIT_COST_SUNDAY * SUNDAY_COPIES * CREDIT_COPIES *1000000000) ) / 1000000000+.0005,4) 作为 TOT_AMT_calc_WORKS

此方法允许我将 BigQuery 中的所有值存储为浮点数,但在运行时执行操作,直到 Google 添加十进制格式。 :)

【讨论】:

    猜你喜欢
    • 2023-03-30
    • 2022-10-14
    • 2012-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-09
    相关资源
    最近更新 更多