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

时间:2016-11-28 18:54:45

标签: sql postgresql greatest-n-per-group

我正在使用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

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

1 个答案:

答案 0 :(得分:1)

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()获取上一个结果,然后从公用表格[cte]

中选择LatestGame = 1的位置

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

要过滤到只有1个用户,您可以执行以下操作,因为您不再需要按user_id对窗口功能进行分区:

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
)

这里只是为了好玩,你可以使用窗口函数来完成它:

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

我认为窗口函数会更适合你。