openlang

reno 的发货比例是 0,百分比也不对,SQLite

每周发货报表。有两处看起来不对,我分不清是不是同一个问题。

查询如下。

SELECT o.warehouse,
       COUNT(*) AS rows_seen,
       COUNT(o.shipped_at) AS with_date,
       COUNT(o.shipped_at) / COUNT(*) AS ship_ratio,
       ROUND(100.0 * COUNT(o.shipped_at) / COUNT(*), 1) AS ship_pct,
       SUM(l.qty) AS units
FROM orders o
JOIN order_lines l ON l.order_id = o.id
GROUP BY o.warehouse
ORDER BY o.warehouse;

输出结果是这样。

fresno|4|3|0|75.0|9
reno|6|4|0|66.7|12
tulsa|3|1|0|33.3|9

ship_ratio 在每个仓库都是 0。我猜是 SQLite 在除法上做了什么?也许吧。

可是 reno 有 4 个订单,只有 2 个有发货日期,我手数的。报表里却是 66.7。直接按仓库数订单的话 reno 是 4,所以 rows_seen 里的 6 我也看不懂。

是不是 shipped_at 为空的待处理订单导致的?这个我还没试过。连先看哪里都不确定……

从英语翻译查看原文

后续讨论 4

顺着彼此的依据读下去
  1. 固定链接

    更新一下,我觉得问题出在 join。每个订单每有一行明细就重复一次,所以 COUNT(*) 数的是明细行,不是订单。也许用 COUNT(DISTINCT o.id) 就能解决?比例那里再加个 1.0 *……

    从英语翻译查看原文
  2. 固定链接

    我觉得问题出在 join 上。每个订单每有一行就会重复一次

    是的,你的更新是对的。那个 6 是行数,不是订单数,COUNT(o.shipped_at) 也是按行在数。Reno 有 2 个已发货订单,合计 4 行,所以得到 4/6 = 66.7。待处理的订单不是原因,它们看起来不对,只是因为已发货的订单行数更多。

    COUNT(DISTINCT o.id) 可以修正分母。分子不要用 COUNT(DISTINCT o.shipped_at),因为同一天发货的两个订单会合并成一个。改成数 id 就行。

    COUNT(DISTINCT o.id) AS orders,
    COUNT(DISTINCT CASE WHEN o.shipped_at IS NOT NULL THEN o.id END) AS shipped,
    1.0 * COUNT(DISTINCT CASE WHEN o.shipped_at IS NOT NULL THEN o.id END)
        / COUNT(DISTINCT o.id) AS ship_ratio

    那个 0 是整数除法,和你猜的一样。两边都是整数时,3/4 得 0。前面加 1.0 * 就够了。

    还有一点。如果“shipped_at 为空”指的是空字符串而不是 NULL,COUNT 会把它当成日期来数。运行 SELECT typeof(shipped_at), COUNT(*) FROM orders GROUP BY 1; 看一下。之后再把报表跑第二遍,确认 reno 是 4 个订单、2 个已发货。

    聚合函数文档见 https://www.sqlite.org/lang_aggfunc.html

    从英语翻译查看原文
  3. 固定链接

    COUNT(DISTINCT CASE WHEN o.shipped_at IS NOT NULL THEN o.id END)

    这样能行,但我不太想这么写。先在子查询里按订单把 qty 加起来,再 join,也许更干净?这样 COUNT(shipped_at) 就保持原样。

    SELECT o.warehouse,
           COUNT(*) AS orders,
           COUNT(o.shipped_at) AS shipped,
           1.0 * COUNT(o.shipped_at) / COUNT(*) AS ship_ratio,
           SUM(t.units) AS units
    FROM orders o
    JOIN (SELECT order_id, SUM(qty) AS units
          FROM order_lines GROUP BY order_id) t ON t.order_id = o.id
    GROUP BY o.warehouse;
    从英语翻译查看原文
  4. 固定链接

    子查询解决了行数膨胀的问题,算 units 的话我会选它而不是 DISTINCT 的写法。但我不会原样换上去,因为内层的 JOIN 会丢掉在 order_lines 里没有任何行的订单。

    你手工数的 reno 是 4 个订单。如果其中有一个还没有明细行(比如待处理订单),它就会从 COUNT(*) 里消失,比率是在一个更小的集合上算的,而且不会报任何错。数字看着对,但统计的对象是错的。第一版报表也把这个问题藏住了,因为它同样用的是内连接。

    FROM orders o
    LEFT JOIN (SELECT order_id, SUM(qty) AS units
               FROM order_lines GROUP BY order_id) t ON t.order_id = o.id

    这样 SUM(t.units) 会忽略 NULL,COUNT(*) 也才真的是订单数。拿 SELECT COUNT(*) FROM orders 和你 orders 列的总和比一下,如果不一样,差的就是这部分。(#3 里的 typeof 检查仍然适用,因为空字符串也会被当成日期来数。)

    从英语翻译查看原文

同一个问题的其他角度

  1. rows.Next 返回了 false,错误却不知道去哪了

    一个基于 database/sql 的小工具返回了半个列表。没有报错。代码如下。 context 带了 deadline。取数据到一半 deadline 到了,rows.Next() 返回 false,循环结束,函数把已经收集到的东西加上 nil 一…

    回复 3最近活动 sqlgo
  2. 部分索引与 WHERE 子句中的参数

    我在给课程项目用 Go 写一个小型任务执行器。环境是通过 database/sql 使用 pgx v5,本地 Postgres 16,还有一张约 600 万行的 jobs 表。几乎全是 done,只有几百行是 queued。我给等待中的行建了个部分索…

    回复 10最近活动 sqlgo
  3. 总计打印出来是数字粘在一起,不是求和

    每日的行都正常(12500、3400、900 won),但 TOTAL 打印出来是 0125003400900 KRW。为什么多了一个 0? tsc 能通过,Postgres 16 跑在 docker 里。reduce 还是一直以来那个写法……

    回复 10最近活动 typescriptsql