繁体   English   中英

Postgres下一个/上一行SQL查询

[英]Postgres Next/Previous row SQL Query

我在Postgres 9.1数据库中有以下表结构,但如果可能,理想的解决方案应该是DB不可知的:

Table: users
|id|username|
|1 |one     |
|2 |two     |
|3 |three   |

Table: items
|id|userid|itemname|created  |
|1 |1     |a       |timestamp|
|2 |1     |b       |timestamp|
|3 |1     |c       |timestamp|
|4 |2     |d       |timestamp|
|5 |2     |e       |timestamp|
|6 |2     |f       |timestamp|
|7 |3     |g       |timestamp|
|8 |3     |h       |timestamp|
|9 |3     |i       |timestamp|

我有一个查询(视图),提供下一个和上一个item.id.

例如

View: UserItems
|id|userid|itemname|nextitemid|previtemid|created  |
|1 |1     |a       |2         |null      |timestamp|
|2 |1     |b       |3         |1         |timestamp|
|3 |1     |c       |4         |2         |timestamp|
|4 |2     |d       |5         |3         |timestamp|
|5 |2     |e       |6         |4         |timestamp|
|6 |2     |f       |7         |5         |timestamp|
|7 |3     |g       |8         |6         |timestamp|
|8 |3     |h       |9         |7         |timestamp|
|9 |3     |i       |null      |8         |timestamp|

我可以使用以下查询执行此操作:

SELECT
  DISTINCT i.id AS id,
  i.userid AS userid,
  i.itemname AS itemname,
  LEAD(i.id) OVER (ORDER BY i.created DESC) AS nextitemid,
  LAG(i.id) OVER (ORDER BY i.created DESC) AS previtemid,
  i.created AS created
FROM items i
  LEFT JOIN users u
  ON i.userid = u.id
ORDER BY i.created DESC;

你能帮忙解决以下问题吗:

1)有没有办法使ids包裹即

  • nextitemid列的最后一行中的NULL itemid应为1
  • previtemid列第一行中的NULL itemid应为9

2)是否存在一种通过用户ID对下一个和前一个itemid进行分组的高效方法,例如

View: UserItems
|id|userid|itemname|nextitemid|previtemid|nextuseritemid|prevuseritemid|created  |
|1 |1     |a       |2         |9         |2             |3             |timestamp|
|2 |1     |b       |3         |1         |3             |1             |timestamp|
|3 |1     |c       |4         |2         |1             |2             |timestamp|
|4 |2     |d       |5         |3         |5             |6             |timestamp|
|5 |2     |e       |6         |4         |6             |4             |timestamp|
|6 |2     |f       |7         |5         |4             |5             |timestamp|
|7 |3     |g       |8         |6         |8             |9             |timestamp|
|8 |3     |h       |9         |7         |9             |7             |timestamp|
|9 |3     |i       |1         |8         |7             |8             |timestamp|

Q1:FIRST_VALUE / LAST_VALUE

Q2:PARTITION BY(正如Roman Pekar已经建议的那样)

在这里看到好闻

SELECT
  DISTINCT i.id AS id,
  i.userid AS userid,
  i.itemname AS itemname,
  COALESCE(LEAD(i.id)        OVER (ORDER BY i.created DESC)
          ,FIRST_VALUE(i.id) OVER (ORDER BY i.created DESC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)) AS nextitemid,
  COALESCE(LAG(i.id)         OVER (ORDER BY i.created DESC)
          ,LAST_VALUE(i.id)  OVER (ORDER BY i.created DESC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)) AS previtemid,
  COALESCE(LEAD(i.id)        OVER (PARTITION BY i.userid ORDER BY i.created DESC)
          ,FIRST_VALUE(i.id) OVER (PARTITION BY i.userid ORDER BY i.created DESC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)) AS nextuseritemid,
  COALESCE(LAG(i.id)         OVER (PARTITION BY i.userid ORDER BY i.created DESC)
          ,LAST_VALUE(i.id)  OVER (PARTITION BY i.userid ORDER BY i.created DESC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)) AS prevuseritemid,
  i.created AS created
FROM items i
  LEFT JOIN users u
  ON i.userid = u.id
ORDER BY i.created DESC;

更新我忘记了PostgreSQL中的first_value和last_value函数 ,多亏了他提醒我的dnoeth。 但是,他的查询不起作用,因为last_value正在使用默认窗口RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW并且不会返回正确的结果,所以你要么必须改变over子句内的范围要么使用first_valueorder by asc

select
    i.id as id,
    i.userid as userid,
    i.itemname as itemname,
    coalesce(
        lead(i.id) over(order by i.created desc),
        first_value(i.id) over(order by i.created desc)
    ) as nextitemid,
    coalesce(
        lag(i.id) over(order by i.created desc),
        first_value(i.id) over(order by i.created asc)
    ) as previtemid,
    coalesce(
        lead(i.id) over(partition by i.userid order by i.created desc),
        first_value(i.id) over(partition by i.userid order by i.created desc)
    ) as nextuseritemid,
    coalesce(
        lag(i.id) over(partition by i.userid order by i.created desc),
        first_value(i.id) over(partition by i.userid order by i.created asc)
    ) as prevuseritemid,
    i.created as created
from items as i
   left outer join users as u on u.id = i.userid
order by i.created desc

sql小提琴演示

以前的版本
我想你可以这样做:

SELECT
  i.id AS id,
  i.userid AS userid,
  i.itemname AS itemname,
  coalesce(
      LEAD(i.id) OVER (ORDER BY i.created DESC),
      (select t.id from items as t order by t.created desc limit 1)
  ) AS nextitemid,
  coalesce(
      LAG(i.id) OVER (ORDER BY i.created DESC),
      (select t.id from items as t order by t.created asc limit 1)
  ) AS previtemid,
  coalesce(
      LEAD(i.id) OVER (partition by i.userid ORDER BY i.created DESC),
      (select t.id from items as t where t.userid = i.userid order by t.created desc limit 1)
  ) AS nextuseritemid,
  coalesce(
      LAG(i.id) OVER (partition by i.userid ORDER BY i.created DESC),
      (select t.id from items as t where t.userid = i.userid order by t.created asc limit 1)
  ) AS prevuseritemid,
  i.created AS created
FROM items i
  LEFT JOIN users u
  ON i.userid = u.id
ORDER BY i.created DESC;

sql小提琴演示

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM