2013-08-17 34 views
8

Tôi đã cấu trúc bảng sau đây trong một cơ sở dữ liệu Postgres 9.1 nhưng giải pháp lý tưởng nên DB thuyết bất khả tri nếu có thể:Postgres Tiếp theo/Trước hàng SQL Query

 
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| 

Tôi có một truy vấn (đối với một cái nhìn) mà cung cấp mục tiếp theo và trước đó.id.

ví dụ:

 
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| 

tôi có thể làm điều này với các truy vấn sau đây:

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; 

Bạn có thể giúp giải quyết các vấn đề sau:

1) Có cách nào để làm cho id quấn tức

  • mục NULL trong hàng cuối cùng của cột nextitemid phải là 1
  • mục NULL trong hàng đầu tiên của cột previtemid phải là 9

2) có cách thực hiện để nhóm các mục tiếp theo và trước đó bởi userid ví dụ:

NB: trong ví dụ này các itemid cho người dùng là tuần tự, đây không phải là trường hợp cho dữ liệu thực, các itemid cho mỗi người dùng được xen kẽ.

 
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| 

Trả lời

8

Q1: FIRST_VALUE/LAST_VALUE

Q2: PARTITION BY (như La Mã Pekar đã gợi ý)

SEE FIDDLE HERE

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; 
+0

+1 cho first_value và last_value :) –

+0

Đó là mẹo độc đáo, cảm ơn rất nhiều chàng trai. Bạn có thể xóa chú thích chưa được kiểm tra quá http://sqlfiddle.com/#!1/58d92/28 – davec

6

cập nhật Tôi đã quên mất first_value and last_value functions trong PostgreSQL, nhờ dnoeth ông nhắc nhở tôi về điều đó. Tuy nhiên, câu hỏi của mình không hoạt động, vì last_value đang làm việc với cửa sổ mặc định RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW và sẽ không trả lại kết quả chính xác, vì vậy bạn có phải thay đổi phạm vi bên trong mệnh đề trên hoặc sử dụng first_value với order 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 fiddle demo

phiên bản trước
tôi nghĩ bạn có thể làm điều này:

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 fiddle demo

+1

Yep, tôi quên ROWS. Nhưng thay vì thay đổi thứ tự sắp xếp, tôi sẽ thay đổi ROWS thành ROWS GIỮA THEO D PRI TIẾP TỤC VÀ KHÔNG BAO GIỜ KHÔNG BAO GIỜ.Điều này sẽ cho phép trình tối ưu hóa thực hiện tất cả các chức năng OLAP với cùng một PARTITION BY và ORDER BY trong một bước duy nhất – dnoeth

+0

yep, làm điều đó và tôi sẽ upvote bạn :) Tôi muốn upvote nó, nhưng nó không hoạt động, vì vậy bạn phải chỉnh sửa nó trước :) –

+0

Được rồi, tôi đã sửa đổi câu trả lời của tôi :-) – dnoeth

Các vấn đề liên quan