减少 CTE 中的记录数 table

Reducing number of records in CTE table

在 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她的最新信息:

# 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)

然后还有一个table存放当前游戏(下面我省略了一些栏目):

# 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 table 的用户信息 - 这样我就可以在游戏面板上方显示用户照片和姓名我的 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-table 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
      ...

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

你应该把它包起来。

word_games 开始,然后加入 words_social table。

您也可以使用 dinstinct on(特定于 postgres)函数来避免第二次 table 查找。

所以最后:

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;

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;