最佳查询取决于数据分布和其他细节。
只要pack_id
子查询中的大多数实际用于连接到packs
(大多数packs
是NOT disabled
),这是非常有效的:
SELECT p.id
, s.online, s.offline, s.depositing, s.withdrawing, s.selling, t.buying
FROM packs p
LEFT JOIN (
SELECT pack_id
, count(status = 'online' OR NULL) AS online
, count(status = 'offline' OR NULL) AS offline
, count(status = 'depositing' OR NULL) AS depositing
, count(status = 'withdrawing' OR NULL) AS withdrawing
, count(status = 'selling' OR NULL) AS selling
FROM stocks
WHERE user_id = #{current_user.id}
AND status = ANY('{online,offline,depositing,withdrawing,selling}'::text[])
GROUP BY 1
) s ON s.pack_id = p.id
LEFT JOIN (
SELECT pack_id, count(*) AS buying
FROM transactions
WHERE status = 'buying'
AND buyer_id = #{current_user.id}
) t ON t.pack_id = p.id
WHERE NOT p.disabled;
在 pg 9.4中,您可以使用聚合 FILTER 子句:
SELECT pack_id
, count(*) FILTER (WHERE status = 'online') AS online
, count(*) FILTER (WHERE status = 'offline') AS offline
, count(*) FILTER (WHERE status = 'depositing') AS depositing
, count(*) FILTER (WHERE status = 'withdrawing') AS withdrawing
, count(*) FILTER (WHERE status = 'selling') AS selling
FROM stocks
WHERE ...
细节:
用于crosstab()
数据透视表以使其更快,但是:
SELECT p.id
, s.online, s.offline, s.depositing, s.withdrawing, s.selling, t.buying
FROM packs p
LEFT JOIN crosstab(
$$
SELECT pack_id, status, count(*)::int AS ct
FROM stocks
WHERE user_id = $$ || #{current_user.id} || $$
AND status = ANY('{online,offline,depositing,withdrawing,selling}'::text[])
GROUP BY 1, 2
ORDER BY 1, 2
$$
,$$SELECT unnest('{online,offline,depositing,withdrawing,selling}'::text[])$$
) s (pack_id int
, online int
, offline int
, depositing int
, withdrawing int
, selling int
) USING (pack_id)
LEFT JOIN (
SELECT pack_id, count(*) AS buying
FROM transactions
WHERE status = 'buying'
AND buyer_id = #{current_user.id}
) t ON t.pack_id = p.id
WHERE NOT p.disabled;
详情在这里:
如果大多数packs
是disabled
,LATERAL
连接会更快(需要 pg 9.3或更高版本):
SELECT p.id
, s.online, s.offline, s.depositing, s.withdrawing, s.selling, t.buying
FROM packs p
LEFT JOIN LATERAL (
SELECT pack_id
, count(status = 'online' OR NULL) AS online
, count(status = 'offline' OR NULL) AS offline
, count(status = 'depositing' OR NULL) AS depositing
, count(status = 'withdrawing' OR NULL) AS withdrawing
, count(status = 'selling' OR NULL) AS selling
FROM stocks
WHERE user_id = #{current_user.id}
AND status = ANY('{online,offline,depositing,withdrawing,selling}'::text[])
AND pack_id = p.id
GROUP BY 1
) s ON TRUE
LEFT JOIN LATERAL (
SELECT pack_id, count(*) AS buying
FROM transactions
WHERE status = 'buying'
AND buyer_id = #{current_user.id}
AND pack_id = p.id
) t ON TRUE
WHERE NOT p.disabled;
为什么LATERAL
?第9.1页中是否有替代方案?