nic*_*ted 3 sql postgresql neo4j greatest-n-per-group sql-limit
以下是我的查询:
SELECT *
FROM (
SELECT f.max, f.min, p.user_id, p.id, p.title, p.rating,
RANK() OVER (
PARTITION BY p.user_id
ORDER BY p.rating DESC, p.id DESC
) AS rnk
FROM posts AS p
INNER JOIN friends AS f ON (p.user_id = f.friend_id)
WHERE f.user_id=1
) AS subq
WHERE (subq.rnk <= subq.max)
LIMIT 10
Run Code Online (Sandbox Code Playgroud)
它会搜索我朋友的帖子,按照评分和日期排序.在此查询中实现的窗口函数允许我根据表上的MAX字段限制为每个朋友返回的行数Friends.
但是,我还有一个字段MIN,用于指定给定朋友的查询所需的最小帖子数.怎么可能?
我也想知道SQL是否是这些类型查询的最佳选择?我已经尝试过Neo4j Graph数据库,虽然它似乎是一个很好的解决方案,但我宁愿避免使用2个独立的数据库.
架构:
CREATE TABLE friends(
user_id int,
friend_id int,
min int,
max int
);
CREATE TABLE posts(
id int,
title varchar(255),
rating int,
date date,
user_id int
);
Run Code Online (Sandbox Code Playgroud)
假设我们有以下数据:
INSERT INTO friends VALUES
(1,2,1,3)
, (1,3,0,5)
, (1,4,2,10);
INSERT INTO posts VALUES
(1, 'posts1', 2, now(), 2)
, (2, 'posts2', 1, now(), 2)
, (3, 'posts3', 5, now(), 2)
, (4, 'posts4', 2, now(), 2)
, (5, 'posts5', 11, now(), 2)
, (6, 'posts6', 7, now(), 2)
, (7, 'posts7', 3, now(), 2)
, (8, 'posts8', 4, now(), 3)
, (9, 'posts9', 1, now(), 3)
, (10, 'posts10', 0, now(), 3)
, (11, 'posts11', 7, now(), 3)
, (12, 'posts12', 3, now(), 3)
, (13, 'posts13', 2, now(), 3)
, (14, 'posts14', 4, now(), 4)
, (15, 'posts15', 9, now(), 4)
, (16, 'posts16', 0, now(), 4)
, (17, 'posts17', 3, now(), 4)
, (18, 'posts18', 2, now(), 4)
, (19, 'posts19', 1, now(), 4)
, (20, 'posts20', 2, now(), 4);
Run Code Online (Sandbox Code Playgroud)
因此(post_id, title, rating, date, friend_id),如果可能,我希望看到具有以下条件的组合:
id= 2id= 3id= 4所以基本上,如果我的朋友friend_id=2发布了1篇或更多文章,我至少想要其中2篇.如果他发表了3篇以上的文章,我希望不超过3篇.
假设我每天都要发来2-5个帖子,如果发帖那么多的话.如果你只张贴一张,那就没关系,我将只有一张贴子.
您在评论中的解释仍然没有加起来.min根据此解释,您的号码将是无效的噪音.
这不是你写的,但这是有道理的:
鉴于帖子的最大显示位置(外部LIMIT),我想min首先从每个朋友那里获得帖子(如果有的话).如果之后有免费插槽,请填写max每位朋友的帖子.
在示例中,如果有更多的插槽仍然可用,那么min将从具有最高优先级的朋友2发布1()帖子,而另一个2(max - min)发布.
如果每个优先级没有足够的插槽,那么哪些帖子可以进行剪切是任意的.我继续并假设应首先选择每个帖子的第一个帖子,等等.
其余的仍然是任意的,但如果您设法制定要求,可以轻松解决.
SELECT *
FROM friends f
, LATERAL (
SELECT *
, row_number() OVER (ORDER BY rating DESC NULLS LAST, id DESC) AS rn
FROM posts p
WHERE user_id = f.friend_id -- LATERAL reference
ORDER BY rating DESC NULLS LAST, date DESC NULLS LAST
LIMIT f.max -- LATERAL reference
) p
WHERE f.user_id = 1
ORDER BY (p.rn > f.min) -- minimum posts from each first
, p.rn
LIMIT 10; -- arbitrary total maximum
Run Code Online (Sandbox Code Playgroud)
假设friends.user_id并posts.id成为主键.你的表定义缺乏.
应该定义所有其他列NOT NULL以使其有意义.
使用联接LATERAL仅max在子查询中为每个朋友选择发布:
使用row_number(),而不是rank()在子查询中.混淆两者是一个常见的错误.
您提到date但它没有显示在您的查询中.也许你真的想要:
, row_number() OVER (ORDER BY rating DESC NULLS LAST
, date DESC NULLS LAST) AS rn
Run Code Online (Sandbox Code Playgroud)DESC NULLS LAST只因为rating而且date可能是NULL:
在Postgres中,您可以在以下位置使用简单的布尔表达式ORDER BY:
ORDER BY (p.rn > f.min), p.rn
Run Code Online (Sandbox Code Playgroud)
这会将min每位朋友的帖子放在首位 第二项(p.rn)给每个朋友一个平等的机会(第一个帖子等).
不要用作标识符.它是标准SQL中的保留字和Postgres中的基本类型名称.date