【问题标题】:Is this the correct way to cast, round, and avg an object in SQL?这是在 SQL 中转换、舍入和平均对象的正确方法吗?
【发布时间】:2020-07-02 22:10:49
【问题描述】:

问题

如何在 PostgreSQL 中使用 avground 提供小数点?

字符串转十进制需要什么函数?

使用的功能

第一个功能

AVG(ratings.dating) AS scent_avg

第一个错误

Error: expected { scents_id: 1, scent_avg: 4.5 } response body, got { scents_id: 1, scent_avg: '4.5000000000000000' }

第二个功能

ROUND(AVG(ratings.rating), 1) AS scent_avg

第二次错误

Error: expected { scents_id: 1, scent_avg: 4.5 } response body, got { scents_id: 1, scent_avg: '4.5' }

第三功能

用什么函数将字符串转换为数字?

编辑

看来我精神错乱了。 sticky bit 澄清了 int 没有小数点!我从原始帖子中删除了 int 位。

【问题讨论】:

  • 用您正在使用的数据库标记您的问题。
  • 预期输出是什么?
  • "...提供带小数点的整数" -- 什么?整数没有小数部分,所以一点没有意义。或者你是什么意思?
  • @Gordon - 同上。添加了 PostgreSQL。
  • 所以结果应该是一个numeric没有微不足道的零?还是格式化字符串 (text)?

标签: sql postgresql casting rounding average


【解决方案1】:

格式化字符串

使用to_char() 获得一个没有无关紧要的零(或填充空白)的格式化字符串 - 正如您评论的那样,“四舍五入到小数点后十位”:

SELECT to_char(round(avg(ratings.rating), 10), 'FM999999999990.9999999999')

注意0。通常,无论如何您都希望获得该职位。喜欢0.3。添加尽可能多的 9 以允许数字。 The manual:

0 指定将始终打印的数字位置,即使它 包含前导/尾随零。 9 还指定了一个数字位置, 但如果它是前导零,那么它将被一个空格替换,而 如果它是一个尾随零并且指定了填充模式,那么它将被删除。

还有about the FM prefix:

填充模式(抑制前导零和填充空白)

没有重要尾随零的数值

强制转换为 double precision (float8) 以获得一个不带微不足道尾随零的数值。演员修剪不重要的零。通常,转换为浮点数会引入极端情况舍入错误。

我首先提出了更复杂的解决方案,但由于您只对精确到小数点后十位的精度感兴趣,而 float8 精确到 15 个小数位,所以这个问题不适用。 The manual:

在所有当前支持的平台上,real 类型的范围为 大约 1E-37 到 1E+37,精度至少为 6 位十进制数字。 double precision 类型的范围约为 1E-307 到 1E+308 至少 15 位的精度。值太大或太大 小会导致错误。如果精度可能会发生舍入 输入数字太高。太接近零的数字不是 可表示为不同于零将导致下溢错误。

就这样:

SELECT round(avg(ratings.rating), 10)::float8

请注意,我们将 进行舍入,因为round() 接受小数位数的变体仅适用于numeric(由于浮点数内部存储的不精确性质) .


在 Postgres 中,您不必太担心那些尾随的零。 The manual:

数值是物理存储的,没有任何额外的前导或 尾随零。因此,列的声明精度和小数位数 是最大值,而不是固定分配。 (在这个意义上,数字类型 更类似于varchar(n) 而不是char(n)。)实际存储 要求是每组四个十进制数字需要两个字节,加上 三到八个字节的开销。

见:

在 Postgres 内部,等式是正确建立的:

SELECT numeric '4.50000000' = numeric '4.5'  -- true
SELECT jsonb '{"scents_id": 4.5}' = jsonb '{"scents_id": 4.5000}'  -- true

您的客户抛出错误似乎是在比较文本表示,这是不正确的。因此,您可能必须按照客户期望的格式进行格式化......

【讨论】:

  • 我明白了。你能解释一下这个输出吗? expected { scents_id: 1, scent_avg: 4.5 } response body, got { scents_id: 1, scent_avg: '4.5000000000000000' }
  • @peyo:您的客户端程序显然会比较 strings(这似乎有点不正确)。将numeric 转换为text 时,默认保留不重要的零。例如,您可以摆脱那些带有to_char() 的人,就像演示的那样。
  • Your client throwing the error seems to compare text representations, which is subtly incorrect. So you may have to format like your client expects ... 这帮助我解决了这个问题。这是我的端点测试:it("GET /scents/:id/ratings responds with 200 and the specified scent rating", () => { const id = 1; const expected = { scents_id: id, scent_avg: JSON.stringify(4.5) } return supertest(app) .get(/scents/${id}/ratings) .expect(200, expected) }); 我在 4.5 左右添加了JSON.stringify,测试通过了。
  • 酷,我们找到了问题的真正根源
【解决方案2】:

在大多数数据库中,这应该可以解决问题:

cast(avg(ratings.ratio * 1.0) as decimal(4, 1))

我在这里看不到任何舍入。从数学上讲,4.545 的平均值。

【讨论】:

  • 最终,数据库中的评分数将不定,所以我想确保它四舍五入到十分位。
  • @peyo 。 . . Postgres 在转换为十进制时四舍五入。同样在 Postgres 中,不需要 * 1.0
猜你喜欢
  • 2021-10-12
  • 2017-09-28
  • 1970-01-01
  • 2012-02-01
  • 2013-07-16
  • 1970-01-01
  • 2014-05-05
  • 1970-01-01
  • 2014-08-27
相关资源
最近更新 更多