这是数据:
me friend game status count
1 2 gem done 10
2 1 gem done 5
1 3 gem done 4
3 1 gem done 6
这是我的疑问:
WITH
-- outgoing for all
outgoing_for_all AS
(SELECT
me,
sum(count) AS sum
FROM game_totals
WHERE status IN ('pending', 'done')
GROUP BY me),
-- incoming for all
incoming_for_all AS
(SELECT
friend,
sum(count) AS sum
FROM game_totals
WHERE status IN ('pending', 'done')
GROUP BY friend)
SELECT
me,
outgoing_for_all.sum AS outgoing,
incoming_for_all.sum AS incoming,
outgoing_for_all.sum - incoming_for_all.sum AS score
FROM outgoing_for_all
FULL OUTER JOIN incoming_for_all ON outgoing_for_all.me = incoming_for_all.friend
结果如下:
me outgoing incoming score
1 14 11 3
2 5 10 -5
3 6 4 2
上面的查询是否可以写入以便更快地执行?
我认为可能只用一个SELECT
进行求和。问题是,我不知道如何正确GROUP BY
所以我可以将count
从两行加成一行。
谢谢。
答案 0 :(得分:2)
是的,您可以使用window functions在一个查询中获得两者的总和。
SELECT
me,
sum(count) AS sum over(partition by me) AS outgoing,
sum(count) AS sum over(partition by friend) AS incoming
FROM game_totals
WHERE status IN ('pending', 'done')
答案 1 :(得分:1)
问题是,我不知道如何正确
GROUP BY
所以我可以将count
从两行加成一行。
您猜对了:因为您希望将一行(count
值)计算两次(一个用于me
的{{1}},一个用于outgoing
' s friend
),您需要将所有行加倍。此外,这些加倍的行将需要按不同的列进行分组。传统的方法通常是incoming
:
UNION
或者,因为我们确切地知道每行必须计算两次,所以您也可以使用SELECT me,
SUM(count) FILTER (WHERE mul = 1) outgoing,
SUM(count) FILTER (WHERE mul = -1) incoming,
SUM(mul * count) score
FROM (
SELECT me, 1 mul, count
FROM game_totals
WHERE status IN ('pending', 'done')
UNION ALL
SELECT friend, -1, count
FROM game_totals
WHERE status IN ('pending', 'done')
) t
GROUP BY me;
:
CROSS JOIN
但是:这些更具可读性。它们实际上比你的变体慢。 (我担心,简单的窗口功能对你来说也无济于事。)我认为你已经找到了最快的解决方案。但是,您应该考虑使用索引(可能更多,或其他索引,如果您已经使用它们)。 F.ex.这个指数可以帮到你很多:
SELECT CASE mul WHEN 1 THEN me ELSE friend END me,
SUM(count) FILTER (WHERE mul = 1) outgoing,
SUM(count) FILTER (WHERE mul = -1) incoming,
SUM(mul * count) score
FROM game_totals, (VALUES (1), (-1)) m(mul)
WHERE status IN ('pending', 'done')
GROUP BY CASE mul WHEN 1 THEN me ELSE friend END