簡體   English   中英

從一個表中選擇所有條目,並從另一個“日志”表中選擇“最新”條目

[英]Select all entries from a table and the LATEST entries from another “logging” table

我試圖為我的問題准備一個SQL Fiddle-

在多人文字游戲中,活動游戲存儲在表words_games

CREATE TABLE words_games (
        gid SERIAL PRIMARY KEY,              /* game id */
        created timestamptz NOT NULL,

        player1 integer REFERENCES words_users(uid) ON DELETE CASCADE NOT NULL,
        player2 integer REFERENCES words_users(uid) ON DELETE CASCADE,

        played1 timestamptz,
        played2 timestamptz,

        score1 integer NOT NULL CHECK(score1 >= 0),
        score2 integer NOT NULL CHECK(score2 >= 0),

        hand1 varchar[7] NOT NULL,
        hand2 varchar[7] NOT NULL,
        pile  varchar[116] NOT NULL,

        letters varchar[15][15] NOT NULL,
        values integer[15][15] NOT NULL,
        bid integer NOT NULL REFERENCES words_boards ON DELETE CASCADE
);

選擇所有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)?

我嘗試了以下INNER JOIN,但它返回了所有動作,而我只需要每局中的最新動作:

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

這不是最快的方法,但是它是更容易理解的方法之一-從時間戳最近的words_moves中獲取所有動作。

現在我們有了它,我們可以使用它來構建查詢以獲取游戲和動作:

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表示公共表表達式 ,這是一種非常方便的子查詢。 除其他外,這意味着如果您最終使用不同的方法來獲取每場比賽的最新舉動( 此問題有很多可供選擇的替代方法可供嘗試),那么很容易進行調入而不會帶來太多麻煩。

希望有幫助!

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;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM