获取 Where in 中每个元素的前一行

Get prior row for each element in Where in

我正在使用 PostgreSQL 数据库。

这里的想法:

这里是数据库的种类:

id | user_id | game_id | mini_game_id | result | created_at
-----------------------------------------------------------
70 | 44      | 105     | 22           | 19     | 28/11/2016
69 | 44      | 105     | 20           | 18     | 28/11/2016

68 | 44      | 104     | 22           | 17     | 27/11/2016
67 | 44      | 104     | 21           | 16     | 27/11/2016

66 | 44      | 103     | 22           | 15     | 26/11/2016
65 | 44      | 103     | 21           | 14     | 26/11/2016
64 | 44      | 103     | 20           | 13     | 26/11/2016

我想在其最新游戏结束时显示每个 mini_game 的结果,以及前一个相同 mini_game 的结果。

需要的结果:

示例:对于 user 44,在其 game 105 的末尾,我想获取此数据:

id | game_id | mini_game_id | second-to-last-result  | date
-----------------------------------------------------------------
68 | 104     | 22           | 17                     | 27/11/2016
64 | 103     | 20           | 13                     | 26/11/2016

我尝试过的:

第一次尝试:

SELECT mini_game_id, 
    array_agg(result) as results,
    array_agg(created_at) as dates
FROM result
WHERE user_id = 44
    AND game_id != 105 --Exclude latest game
GROUP BY mini_game_id
ORDER BY mini_game_id

结果:

mini_game_id | results  | dates 
--------------------------------------------------
22           | {17, 15} | {27/11/2016, 26/11/2016}
21           | {16, 14} | {27/11/2016, 26/11/2016}
20           | {13}     | {26/11/2016}

这里的问题是我为一个用户获取每个游戏的每个 mini_game 结果,这对我来说似乎太过分了,因为我可以有数千个结果...

第二次尝试:

SELECT id, 
    game_id,
    mini_game_id, 
    result,
    created_at
FROM result
WHERE user_id = 44
    AND game_id != 105 --Exclude latest game
    AND mini_game IN (22, 20) --The two mini-games the user have played in game 105
ORDER BY created_at DESC
LIMIT 1

结果:

id | game_id | mini_game_id | result  | created_at
--------------------------------------------------
68 | 104     | 22           | 17      | 27/11/2016

问题:

显然,我只得到 1 个结果。但是我的想法是 limit 1 WHERE IN

中的每个值

你能帮我理解我是怎么做到的吗?
谢谢

WITH cte AS (
    SELECT *
       ,DENSE_RANK() OVER (PARTITION by user_id ORDER BY created_at DESC) as LatestGame
       ,LAG(result) OVER (PARTITION BY user_id, mini_game_id ORDER BY created_at) as SecondToLastResult
    FROM
       Table
)

SELECT
    id
    ,game_id
    ,mini_game_id
    ,result
    ,SecondToLastResult
    ,created_at as Date
FROM
    cte
WHERE
    LatestGame = 1
;

使用 DENSE_RANK() 定义最新游戏,使用 LAG() 获取之前的结果,然后 select 其中 LatestGame = 1 来自常见的 table 表达式 [ cte]

这是 postgresql 的 link 关于 window 函数:https://www.postgresql.org/docs/8.4/static/functions-window.html

要仅筛选 1 个用户,您可以执行以下操作,因为您将不再需要按 user_id 划分 window 函数:

WITH cte AS (
    SELECT *
       ,DENSE_RANK() OVER (ORDER BY created_at DESC) as LatestGame
       ,LAG(result) OVER (PARTITION BY mini_game_id ORDER BY created_at) as SecondToLastResult
    FROM
       Table
    WHERE
       user_id = 44
)

这里只是为了好玩,您可以如何使用 window 函数来做到这一点:

SELECT
    t.id
    ,t.user_id
    ,t.game_id
    ,t.mini_game_id
    ,t.result
    ,t.created_at
    ,(SELECT result
             FROM
                Table t2
            WHERE
                t.user_id = t2.user_id
                AND t.mini_game_id = t2.mini_game_id
                AND t.created_at > t2.created_at
            ORDER BY
                t2.created_at DESC
            LIMIT 1) as SecondToLastResult
FROM
    Table t
    LEFT JOIN Table t1
    ON t.user_id = t1.user_id
    AND t.created_at < t1.created_at
WHERE
    t1.id IS NULL
    AND t.user_id = 44

不过我认为 window 函数对您来说会更好。