Sql 从一个表中选择所有条目,从另一个“日志”表中选择最新条目

Sql 从一个表中选择所有条目,从另一个“日志”表中选择最新条目,sql,postgresql,join,common-table-expression,postgresql-9.5,Sql,Postgresql,Join,Common Table Expression,Postgresql 9.5,我试着为我的问题准备一个解决方案- 在多人文字游戏中,活动游戏存储在“文字游戏”表中: 并且可以很容易地选择id为1的玩家参与的所有游戏: SELECT * FROM words_games WHERE player1 = 1 OR player2 = 1; 但现在我还添加了一个表words_moves,作为玩家动作的日志记录: CREATE TYPE words_action AS ENUM ('play', 'skip', 'swap', 'resign'); CREATE TABLE

我试着为我的问题准备一个解决方案-

在多人文字游戏中,活动游戏存储在“文字游戏”表中:

并且可以很容易地选择id为1的玩家参与的所有游戏:

SELECT * FROM words_games WHERE player1 = 1 OR player2 = 1;
但现在我还添加了一个表words_moves,作为玩家动作的日志记录:

CREATE TYPE words_action AS ENUM ('play', 'skip', 'swap', 'resign');

CREATE TABLE words_moves (
        mid SERIAL PRIMARY KEY,             /* move id */
        action words_action NOT NULL,
        gid integer NOT NULL REFERENCES words_games ON DELETE CASCADE,
        uid integer NOT NULL REFERENCES words_users ON DELETE CASCADE,
        played timestamptz NOT NULL,
        tiles jsonb NULL,
        score integer NULL CHECK(score > 0) /* score awarded in that move */
);
现在,当一个用户连接到我的游戏服务器时,我不仅要向她发送所有活动的游戏,还要发送每个游戏最高mid的最新动作

如何在一个查询中运行这样的连接或CTE

我尝试了以下内部连接,但它会返回所有移动,而我只需要每个游戏中的最新移动:

SELECT
    g.gid,
    EXTRACT(EPOCH FROM g.created)::int AS created,
    g.player1,
    COALESCE(g.player2, 0) AS player2,
    COALESCE(EXTRACT(EPOCH FROM g.played1)::int, 0) AS played1,
    COALESCE(EXTRACT(EPOCH FROM g.played2)::int, 0) AS played2,
    ARRAY_TO_STRING(g.hand1, '') AS hand1,
    ARRAY_TO_STRING(g.hand2, '') AS hand2,
    -- g.letters,
    -- g.values,
    m.action,
    m.tiles                                                                                                                                                                   
FROM words_games g INNER JOIN words_moves m                                                                                                                                          
    ON g.gid = m.gid                                                                                                                                                                  
    AND ( g.player1 = m.uid OR g.player2 = m.uid )                                                                                                                                     
    AND ( g.player1 = 1 OR g.player2 = 1 )                                                                                                                                              
ORDER BY g.gid;


     gid |  created   | player1 | player2 |  played1   |  played2   |  hand1  |  hand2  | action |                                                                                                                                                                          tiles                                                                                                                                                                          
    -----+------------+---------+---------+------------+------------+---------+---------+--------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
       1 | 1471794994 |       1 |       2 | 1471868012 | 1471810486 | ПЕАЯСАС | ЖИОБАЯС | play   | [{"col": 7, "row": 10, "value": 1, "letter": "Н"}, {"col": 7, "row": 8, "value": 2, "letter": "К"}, {"col": 7, "row": 9, "value": 1, "letter": "И"}, {"col": 7, "row": 7, "value": 2, "letter": "С"}]
       1 | 1471794994 |       1 |       2 | 1471868012 | 1471810486 | ПЕАЯСАС | ЖИОБАЯС | play   | [{"col": 7, "row": 14, "value": 2, "letter": "К"}, {"col": 7, "row": 13, "value": 1, "letter": "Н"}, {"col": 7, "row": 11, "value": 3, "letter": "У"}, {"col": 7, "row": 12, "value": 2, "letter": "П"}]
       1 | 1471794994 |       1 |       2 | 1471868012 | 1471810486 | ПЕАЯСАС | ЖИОБАЯС | play   | [{"col": 6, "row": 2, "value": 2, "letter": "П"}, {"col": 6, "row": 3, "value": 1, "letter": "О"}, {"col": 6, "row": 4, "value": 1, "letter": "Е"}, {"col": 6, "row": 5, "value": 5, "letter": "Ж"}, {"col": 6, "row": 6, "value": 5, "letter": "Ы"}, {"col": 6, "row": 7, "value": 2, "letter": "П"}, {"col": 6, "row": 8, "value": 5, "letter": "Ы"}]
       2 | 1471795037 |       1 |       2 | 1471806484 | 1471865696 | КЙВГКСМ | ЯРХЖИМН | swap   | "А"
       2 | 1471795037 |       1 |       2 | 1471806484 | 1471865696 | КЙВГКСМ | ЯРХЖИМН | play   | [{"col": 7, "row": 10, "value": 5, "letter": "Ы"}, {"col": 7, "row": 9, "value": 2, "letter": "Д"}, {"col": 7, "row": 8, "value": 1, "letter": "А"}, {"col": 7, "row": 7, "value": 2, "letter": "Л"}]
    (5 rows)
更新:


事实上,我需要一个左键连接,因为可以有没有任何玩家移动的游戏…

好的,让我们建立sql。首先,我们需要找出所有游戏的最新动向。有很多方法可以做到这一点,但让我们试试这个:

SELECT *
FROM words_moves wm1
WHERE
  played = (SELECT max(played)
            FROM words_moves wm2
            WHERE wm1.gid = wm2.gid);
这不是最快的方法,但它是最容易理解的方法之一——从单词中获取每一个动作,其中时间戳是最新的

现在我们有了它,我们可以用它构建一个查询,以获得游戏和移动:

WITH last_moves AS (
  SELECT *
  FROM words_moves wm1
  WHERE
    played = (SELECT max(played)
              FROM words_moves wm2
              WHERE wm1.gid = wm2.gid))
SELECT *
FROM words_games wg
  LEFT JOIN last_moves lm
    ON (wg.gid = lm.gid)
WHERE
  player1 = 1 OR
  player2 = 1;
如果您不熟悉,那么WITH there表示一个非常方便的子查询。除此之外,这意味着如果你最终使用不同的方法来获得每场游戏的最新移动,并且有一套很好的备选方案可以尝试,那么很容易就可以轻松地进行交换,而不会有太多麻烦


希望有帮助

好的,让我们构建sql。首先,我们需要找出所有游戏的最新动向。有很多方法可以做到这一点,但让我们试试这个:

SELECT *
FROM words_moves wm1
WHERE
  played = (SELECT max(played)
            FROM words_moves wm2
            WHERE wm1.gid = wm2.gid);
SELECT g.gid
    , EXTRACT(EPOCH FROM g.created)::int AS created
    , g.player1
    , COALESCE(g.player2, 0) AS player2
    , COALESCE(EXTRACT(EPOCH FROM g.played1)::int, 0) AS played1
    , COALESCE(EXTRACT(EPOCH FROM g.played2)::int, 0) AS played2
    , ARRAY_TO_STRING(g.hand1, '') AS hand1
    , ARRAY_TO_STRING(g.hand2, '') AS hand2
    , m.action
    , m.tiles 
FROM words_games g  
LEFT JOIN words_moves m
    ON g.gid = m.gid
        -- this is redundant: m.gid is a FK
        -- AND (g.player1 = m.uid OR g.player2 = m.uid)
    AND NOT EXISTS ( -- suppress all-but-the-last
        SELECT * FROM words_moves nx
        WHERE nx.gid = g.gid -- Same game
          -- AND nx.mid > m.mid   -- but a higher moveid
                                  -- (assuming ascending move_ids)
          -- or: you could use m.played, if that is ascending
        AND nx.played > m.played
        )
WHERE (g.player1 = 1 OR g.player2 = 1)
ORDER BY g.gid;
这不是最快的方法,但它是最容易理解的方法之一——从单词中获取每一个动作,其中时间戳是最新的

现在我们有了它,我们可以用它构建一个查询,以获得游戏和移动:

WITH last_moves AS (
  SELECT *
  FROM words_moves wm1
  WHERE
    played = (SELECT max(played)
              FROM words_moves wm2
              WHERE wm1.gid = wm2.gid))
SELECT *
FROM words_games wg
  LEFT JOIN last_moves lm
    ON (wg.gid = lm.gid)
WHERE
  player1 = 1 OR
  player2 = 1;
如果您不熟悉,那么WITH there表示一个非常方便的子查询。除此之外,这意味着如果你最终使用不同的方法来获得每场游戏的最新移动,并且有一套很好的备选方案可以尝试,那么很容易就可以轻松地进行交换,而不会有太多麻烦


希望有帮助

要检测最新的移动,您必须在日志文件中放置时间戳。中间序列也可以起到同样的作用,但要检测最新的移动似乎是一个坏习惯,您必须在日志文件中放置一个时间戳。中期系列也可以起到同样的作用,但是为了让你更好地理解这个建议而欺骗你,这看起来是一个坏习惯。经过一番思考,我决定将最后一列添加到words\u games中,并在我将新日志记录插入words\u moves table INSERT时进行更新。。。。返回midAlso我想你的意思是wm1.gid=wm2.gid和wm1.uid=1I不想限制单词_moves.uid,因为我假设你想显示最后一步,即使是对手的。如果你想朝这个方向发展,你可以添加像和wm1.uid=1或wm2.uid=1这样的内容。谢谢你的建议。经过一番思考,我决定将最后一列添加到words\u games中,并在我将新日志记录插入words\u moves table INSERT时进行更新。。。。返回midAlso我想你的意思是wm1.gid=wm2.gid和wm1.uid=1I不想限制单词_moves.uid,因为我假设你想显示最后一步,即使是对手的。如果您想朝这个方向走,您可以添加像和wm1.uid=1或wm2.uid=1这样的内容。
SELECT g.gid
    , EXTRACT(EPOCH FROM g.created)::int AS created
    , g.player1
    , COALESCE(g.player2, 0) AS player2
    , COALESCE(EXTRACT(EPOCH FROM g.played1)::int, 0) AS played1
    , COALESCE(EXTRACT(EPOCH FROM g.played2)::int, 0) AS played2
    , ARRAY_TO_STRING(g.hand1, '') AS hand1
    , ARRAY_TO_STRING(g.hand2, '') AS hand2
    , m.action
    , m.tiles 
FROM words_games g  
LEFT JOIN words_moves m
    ON g.gid = m.gid
        -- this is redundant: m.gid is a FK
        -- AND (g.player1 = m.uid OR g.player2 = m.uid)
    AND NOT EXISTS ( -- suppress all-but-the-last
        SELECT * FROM words_moves nx
        WHERE nx.gid = g.gid -- Same game
          -- AND nx.mid > m.mid   -- but a higher moveid
                                  -- (assuming ascending move_ids)
          -- or: you could use m.played, if that is ascending
        AND nx.played > m.played
        )
WHERE (g.player1 = 1 OR g.player2 = 1)
ORDER BY g.gid;