减少CTE表中的记录数

时间:2016-10-21 13:46:47

标签: sql postgresql common-table-expression postgresql-9.5

在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
      ...

因为我还需要游戏对手的信息(给定和姓氏,照片)。

2 个答案:

答案 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;