在PostgreSQL 9.5.4中,我保留了各种社交网络的播放器信息:
# TABLE words_social;
sid | social | female | given | family | photo | place | stamp | uid
-------+--------+--------+---------+--------+-------+-------+------------+-----
aaaaa | 1 | 0 | Abcde1 | | | | 1470237061 | 1
aaaaa | 2 | 0 | Abcde2 | | | | 1477053188 | 1
aaaaa | 3 | 0 | Abcde3 | | | | 1477053330 | 1
kkkkk | 3 | 0 | Klmnop3 | | | | 1477053810 | 2
kkkkk | 4 | 0 | Klmnop4 | | | | 1477053857 | 2
ggggg | 2 | 0 | Ghijk2 | | | | 1477053456 | 3
ggggg | 3 | 0 | Ghijk3 | | | | 1477053645 | 3
ggggg | 4 | 0 | Ghijk4 | | | | 1477053670 | 3
xxxxx | 4 | 0 | Xyzok | | | | 1470237393 | 4
(9 rows)
social
列中的1,2,3,4值表示" Facebook"," Twitter"等
对于玩家,我总是可以通过以下方式选择最近的信息:
# select * from words_social s1 WHERE stamp =
(SELECT max(stamp) FROM words_social s2 WHERE s1.uid = s2.uid);
sid | social | female | given | family | photo | place | stamp | uid
-------+--------+--------+---------+--------+-------+-------+------------+-----
aaaaa | 3 | 0 | Abcde3 | | | | 1477053330 | 1
kkkkk | 4 | 0 | Klmnop4 | | | | 1477053857 | 2
ggggg | 4 | 0 | Ghijk4 | | | | 1477053670 | 3
xxxxx | 4 | 0 | Xyzok | | | | 1470237393 | 4
(4 rows)
然后还有另一个存储当前游戏的表(我在下面省略了一些列):
# select gid, created, finished, player1, player2 from words_games;
gid | created | finished | player1 | player2
-----+-------------------------------+----------+---------+---------
1 | 2016-10-21 14:51:12.624507+02 | | 4 | 1
2 | 2016-10-21 14:51:22.631507+02 | | 3 |
(2 rows)
每当用户(例如uid
1)连接到服务器时,我都会向她发送她参与的游戏:
# select gid, created, finished, player1, player2 from words_games where player1 = 1
union select gid, created, finished, player2, player1 from words_games where player2 = 1;
gid | created | finished | player1 | player2
-----+-------------------------------+----------+---------+---------
1 | 2016-10-21 14:51:12.624507+02 | | 4 | 1
(1 row)
我的问题:对于上面的UNION SELECT语句,我需要从words_social
表添加用户信息 - 这样我就可以在我的2人游戏中在游戏板上方显示用户照片和名称。
所以我尝试使用CTE(并使用用户名添加i.given
列):
# with user_infos AS (select * from words_social s1 WHERE stamp =
(SELECT max(stamp) FROM words_social s2 WHERE s1.uid = s2.uid))
select g.gid, g.created, g.finished, g.player1, g.player2, i.given from words_games g join user_infos i on (g.player1=i.uid) where g.player1 = 1
union select g.gid, g.created, g.finished, g.player2, g.player1, i.given from words_games g join user_infos i on (g.player2=i.uid) where g.player2 = 1;
gid | created | finished | player1 | player2 | given
-----+-------------------------------+----------+---------+---------+--------
1 | 2016-10-21 14:51:12.624507+02 | | 1 | 4 | Abcde3
(1 row)
这很好用,但我仍有以下问题 -
我担心一旦我的游戏中有很多玩家,CTE表格user_infos
会变得非常大。
如何重写查询,以便user_infos
仅保存相关记录?
我不能只执行
# with user_infos AS (select * from words_social s1 WHERE stamp =
(SELECT max(stamp) FROM words_social s2 WHERE s1.uid = s2.uid))
AND s1.uid = 1
...
因为我还需要游戏对手的信息(给定和姓氏,照片)。
答案 0 :(得分:2)
user_infos
查询可以重写并使用如下:
with user_infos as (
select row_number() over (partition by uid order by stamp desc), * from words_social
)
select g.gid, g.created, g.finished, g.player1, g.player2, i.given from words_games g
join user_infos i on g.player1=i.uid and i.row_number = 1 and g.player1 = 1
union select g.gid, g.created, g.finished, g.player2, g.player1, i.given from words_games g
join user_infos i on g.player2=i.uid and i.row_number =1 and g.player2 = 1;
答案 1 :(得分:1)
你应该用另一种方式包装它。
从word_games
开始,然后使用words_social
表格进行加入。
此外,您可以使用dinstinct on
(特定于postgres)函数来避免第二次表查找。
所以最后:
with game_finder as (
select g.gid, g.player1, g.player2
from words_games g where g.player1 = 1
union
select g.gid,g.player2, g.player1
from words_games g where g.player2 = 1),
player1_infos as (
select distinct on (uid)
gf.gid,
uid,
social,
given
from words_social ws
inner join game_finder gf on gf.player1 = ws.uid
ORDER BY uid, stamp DESC
),
player2_infos as (
select gf.gid,
uid,
social,
given
from words_social ws
inner join game_finder gf on gf.player2 = ws.uid
ORDER BY uid, stamp DESC
)
select *
from game_finder gf
left outer join player1_infos p1 on gf.gid = p1.gid
left outer join player2_infos p2 on gf.gid = p2.gid;