每日的行都正常(12500、3400、900 won),但 TOTAL 打印出来是 0125003400900 KRW。为什么多了一个 0?
const grand = rows.reduce((acc, r) => acc + r.cents, 0);
console.log(`TOTAL ${won(grand)}`);
tsc 能通过,Postgres 16 跑在 docker 里。reduce 还是一直以来那个写法……
每日的行都正常(12500、3400、900 won),但 TOTAL 打印出来是 0125003400900 KRW。为什么多了一个 0?
const grand = rows.reduce((acc, r) => acc + r.cents, 0);
console.log(`TOTAL ${won(grand)}`);
tsc 能通过,Postgres 16 跑在 docker 里。reduce 还是一直以来那个写法……
Daily lines are fine (12500, 3400, 900 won), but TOTAL prints 0125003400900 KRW. Why extra zero?
const grand = rows.reduce((acc, r) => acc + r.cents, 0);
console.log(`TOTAL ${won(grand)}`);
tsc passes, Postgres 16 in docker. Same reduce as always...
cents 是 bigint 还是 numeric 列?驱动会把这些作为字符串返回,而 0 + "12500" 是 "012500"。tsc 看不出来。
Is cents a bigint or numeric column? The driver hands those back as strings, and 0 + "12500" is "012500". tsc can't see that.
对,基本就是这个原因,0125003400900 就是字符串拼接的结果,pg 把 bigint 和 numeric 作为字符串返回。要么在 reduce 里用 Number(r.cents),要么在查询里用 cents::int 转换(如果值放得下)。如果可能很大,就用 BigInt,最后再格式化。
yeah that's almost certainly it, 0125003400900 is just the strings concatenated, pg returns bigint and numeric as strings. either Number(r.cents) in the reduce, or cast in the query with cents::int if the values fit. if they might get big, use BigInt and format at the end.
没什么问题,你的总计只是非常诚实。Postgres 发的是文本,JavaScript 把它们粘起来。用字符串拼接做会计,tsc 说“看起来没问题”。当然了。
Nothing is wrong, your total is just very honest. Postgres sends text, JavaScript glues it. Accounting by string concatenation, and tsc says "looks fine". Of course it does.
有道理,不过真正的解法是把行类型写成 cents: string,这样 tsc 会在 + 那里报错,这里撒谎的是驱动的类型。
fair, though the real fix is typing the row as cents: string so tsc complains at the +, the driver types are what lie here.
行,怪驱动吧。类型也会撒谎。
是啊,类型会撒谎,但至少字符串在精神上是加起来了的
关于 cents::int 有一点要注意,如果你改在 SQL 里求和,结果又会变回字符串。sum(int) 返回 bigint,sum(bigint) 返回 numeric,驱动把这两种都作为文本给你。所以 Number() 还是要用,只是最后用一次,而不是每行都用。
在查询里求和仍然是更好的位置,数据库一趟就搞定。聚合函数的返回类型在 Postgres 聚合函数文档 里。
One caveat on cents::int: if you sum in SQL instead, the result is a string again. sum(int) returns bigint and sum(bigint) returns numeric, and the driver gives you both as text. So the Number() is needed anyway, just once at the end instead of per row.
Summing in the query is still the better place, the database does it in one pass. The aggregate return types are in the Postgres aggregate docs.
或者用 pg-protocol 里的 setTypeParser(20, Number) 注册一次解析器,所有 int8 就都以数字返回。值超过 2^53 之前都没问题。
Or register a parser once with setTypeParser(20, Number) from pg-protocol, and every int8 comes back as a number. Just fine until values pass 2^53.
好笑的是,每日的行看着正常,是因为 won() 对字符串“12500”和数字的格式化结果一样。只有 + 暴露了问题。前面多出来的 0 就是我初始的 0 变成了文本。MDN 的加法运算符页面说得很直白,任意一边是字符串就拼接。这个我是知道的,只是从没在 reduce 里遇到过...
Funny part is the daily lines looked fine because won() formats the string "12500" the same as a number. Only the + showed it. And the extra zero in front was just my initial 0 becoming text. MDN's addition operator page says it plainly, if either side is a string, concatenate. I knew this, but never in a reduce...
对,这适合我的情况,一处搞定,不用每个 reduce 都改。不过我觉得 setTypeParser 是来自 pg-types,不是 pg-protocol...
Yes, this fits my case, one place instead of every reduce. I think setTypeParser comes from pg-types though, not pg-protocol...
一个基于 database/sql 的小工具返回了半个列表。没有报错。代码如下。 context 带了 deadline。取数据到一半 deadline 到了,rows.Next() 返回 false,循环结束,函数把已经收集到的东西加上 nil 一…
我在给课程项目用 Go 写一个小型任务执行器。环境是通过 database/sql 使用 pgx v5,本地 Postgres 16,还有一张约 600 万行的 jobs 表。几乎全是 done,只有几百行是 queued。我给等待中的行建了个部分索…
任务队列直接放在表里。一个 worker 用 Python,一个用 Go。BEGIN,然后 SELECT id, payload FROM jobs WHERE status = 'queued' ORDER BY id LIMIT 1 FOR UP…