Postgres 中的组合 SQL 计数查询

Combinational SQL count query in Postgres

我有一些电台存储在一个简单的 stations collection:

+----+-----------+
| id | name      |
+----+-----------+
| 1  | Station A |
+----+-----------+
| 2  | Station B |
+----+-----------+
| 3  | Station C |
+----+-----------+
| 4  | Station D |
+----+-----------+

我在 rides collection:

中存储了一些游乐设施
+----+---------------+-------------+
| id | fromStationId | toStationId |
+----+---------------+-------------+
| 1  | 3             | 4           |
+----+---------------+-------------+
| 2  | 2             | 1           |
+----+---------------+-------------+
| 3  | 1             | 1           |
+----+---------------+-------------+
| 4  | 3             | 2           |
+----+---------------+-------------+

我想在所有可能的 fromStation 姓名和 toStation 姓名对之间创建所有 inter-station 游乐设施的计数列表,结果如下所示:

[
  {
    "fromStation": "Station A",
    "toStation": "Station A",
    "count": 1196
  },
  {
    "fromStation": "Station A",
    "toStation": "Station B",
    "count": 1
  },
  {
    "fromStation": "Station A",
    "toStation": "Station C",
    "count": 173
  },
]

And so on for all other combinations...

如何获得所有可能的 two-pair 车站名称组合,然后计算其中的乘车次数?我正在使用最新版本的 Postgres。

demos:db<>fiddle

SELECT 
    c.from_station,
    c.to_station,
    COUNT(*)
FROM stations s1
JOIN stations s2 ON s1.station <> s2.station                                     -- 1
JOIN connections c ON s1.station = c.from_station AND s2.station = c.to_station  -- 2
GROUP BY c.from_station, c.to_station                                            -- 3
  1. 在站上创建一个自加入table。 <> 加入条件确保不会加入同一站。所有其他站点将相互连接,从而创建所有组合
  2. 在您的连接table上加入这个结果,使用来自连接fromto点的自连接的两个站点列
  3. 现在您可以按 fromtoCOUNT(*) 聚合这个。

如果你想识别from站等于to站的情况,你可以改变

JOIN stations s2 ON s1.station <> s2.station

简单化为:

CROSS JOIN stations s2

如果你想获得问题中显示的 JSON 对象:

SELECT 
    json_agg(connection)                      -- 2
FROM (
    SELECT 
        json_build_object(                    -- 1
            'fromStation', c.from_station,
            'toStation', c.to_station,
            'count', COUNT(*)
        ) as connection
    FROM stations s1
    JOIN stations s2 ON s1.station <> s2.station
    JOIN connections c ON s1.station = c.from_station AND s2.station = c.to_station
    GROUP BY c.from_station, c.to_station
) s
  1. 根据您在上面创建的列创建 JSON 对象
  2. 将它们聚合成一个 JSON 数组。

先聚合游乐设施,然后将 ID 解析为名称:

SELECT f.name AS from_station, t.name AS to_station, count
FROM  (
   SELECT from_station_id, to_station_id, count(*) AS count
   FROM   rides
   GROUP  BY 1, 2
   ) r
JOIN   stations f ON f.id = r.from_station_id
JOIN   stations t ON t.id = r.to_station_id
ORDER  BY 1, 2;  -- optional order

当然,这只会产生与实际游乐设施的组合。如果您需要包含没有任何游乐设施的组合,则需要 OUTER 连接到 stations table 本身的笛卡尔积。类似于:

-- include all combinations (even without rides)
SELECT from_station, to_station, COALESCE(count, 0) AS count
FROM  (
   SELECT from_station_id, to_station_id, count(*) AS count
   FROM   rides
   GROUP  BY 1, 2
   ) r
RIGHT  JOIN (
   SELECT f.id AS from_id, f.name AS from_station
        , t.id AS to_id  , t.name AS to_station
   FROM   stations f CROSS JOIN stations t
   ) s ON  s.from_id = r.from_station_id
      AND  s.to_id   = r.to_station_id
ORDER  BY 1, 2;  -- optional order

同样, 加入车站之前汇总游乐设施更便宜。

要将其包装为 JSON 数组或记录,只需:

SELECT json_agg(sub)
FROM  (
   -- query from above
   ) sub;

db<>fiddle here