每组最小行数
Minimum number of rows per group
以下是我的查询:
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
它搜索我朋友的帖子,按他们的评分和日期排序。此查询中实现的 window 函数允许我根据 Friends
table 上的 MAX
字段限制为每个朋友返回的行数。
但是,我还有一个字段 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
);
假设我们有以下数据:
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);
因此,如果可能,我希望看到 (post_id, title, rating, date, friend_id)
具有以下条件的组合:
- 来自
id
=2 的朋友的 1 到 3 个帖子
- 来自
id
=3 好友的 0 到 5 个帖子
- 来自
id
=4 好友的 2 到 10 个帖子
所以基本上,如果我 friend_id=2
的朋友发表了 1 篇或多篇文章,我至少要 2 篇。如果他发的文章超过3篇,我要的不超过3篇。
我错过了什么吗?不能在 where
子句中添加 and subq.rnk >= subq.min
吗?
SELECT *
FROM (SELECT f.max, f.min, p.friend_id, p.id, p.title, p.rating
row_number() over (partition by p.user_id
order by p.rating DESC, p.id DESC
) as rnk
FROM posts p INNER JOIN
friends f
ON p.friend_id = f.user_id
WHERE f.user_id = 1
) subq
WHERE subq.rnk <= subq.max and subq.rnk >= subq.min
我觉得从neo4j/cypher的角度来看,这确实是你想要做的...
match (u:User {id: 1})-[r:FOLLOWS]->(p:Publisher)
with u, p, r
match p-[:PUBLISHED]-(i:Item)
with u, p, r, i
order by i.name
return u.name, p.name, i.name
skip 5
limit 2
您只需要参数化最小值和最大值并在 运行 时绑定它们,这将涉及两个查询而不是一个,但我认为这仍然是一个优雅的解决方案。我曾尝试在 skip
和 limit
中包含属性,但 cypher 显然不支持(目前)。它需要一个参数或一个无符号整数。
match (u:User {id: 1})-[r:FOLLOWS]->(p:Publisher)
with u, p, r
match p-[:PUBLISHED]-(i:Item)
with u, p, r, i
order by i.name
return u.name, p.name, i.name
skip {min}
limit {max}
Let's say I want to have 2-5 posts from you everyday, if you post that
much. If you post only one, it is alright, and I will have the only
one post.
你的解释还是不成立。根据此解释,您的 min
号码将是无效的噪音。
这不是你写的,但这是有道理的:
给定 post 的最大显示槽(外层 LIMIT
),我想得到 min
post 首先来自每个朋友(如果有的话)。如果在那之后还有空位,每个朋友最多填max
posts。
在示例中,来自朋友 2 的 1 (min
) post 具有最高优先级,另外 2 (max - min
) posts 如果有更多插槽仍然可用。
如果每个优先级都没有足够的空位,那么 post 可以任意选择。我继续假设每个人的第一个 post 应该首先 selected,等等
剩下的仍然是随意的,但是如果您设法制定需求就可以轻松解决。
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
注释
假设 friends.user_id
和 posts.id
为主键。那里缺少您的 table 定义。
应定义所有其他列 NOT NULL
以使其有意义。
仅使用 LATERAL
加入 select max
postings per friend in the subquery:
在子查询中使用row_number()
, not rank()
。混淆两者是一个常见的错误。
您提到了 date
但它没有显示在您的查询中。也许你真的想要:
, row_number() OVER (ORDER BY rating DESC NULLS LAST
, date DESC NULLS LAST) AS rn
DESC NULLS LAST
只是因为 rating
和 date
可能为 NULL:
- PostgreSQL sort by datetime asc, null first?
在 Postgres 中,您可以在 ORDER BY
:
中使用简单的布尔表达式
ORDER BY (p.rn > f.min), p.rn
- SQL select query order by day and month
- Sorting null values after all others, except special
每个朋友的 min
post 放在第一位。第二项(p.rn
)给每个朋友一个平等的机会(第一个post第一个等等)。
不要使用 date
作为标识符。它是标准中的保留字 SQL 和 Postgres 中的基本类型名称。
以下是我的查询:
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
它搜索我朋友的帖子,按他们的评分和日期排序。此查询中实现的 window 函数允许我根据 Friends
table 上的 MAX
字段限制为每个朋友返回的行数。
但是,我还有一个字段 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
);
假设我们有以下数据:
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);
因此,如果可能,我希望看到 (post_id, title, rating, date, friend_id)
具有以下条件的组合:
- 来自
id
=2 的朋友的 1 到 3 个帖子
- 来自
id
=3 好友的 0 到 5 个帖子
- 来自
id
=4 好友的 2 到 10 个帖子
所以基本上,如果我 friend_id=2
的朋友发表了 1 篇或多篇文章,我至少要 2 篇。如果他发的文章超过3篇,我要的不超过3篇。
我错过了什么吗?不能在 where
子句中添加 and subq.rnk >= subq.min
吗?
SELECT *
FROM (SELECT f.max, f.min, p.friend_id, p.id, p.title, p.rating
row_number() over (partition by p.user_id
order by p.rating DESC, p.id DESC
) as rnk
FROM posts p INNER JOIN
friends f
ON p.friend_id = f.user_id
WHERE f.user_id = 1
) subq
WHERE subq.rnk <= subq.max and subq.rnk >= subq.min
我觉得从neo4j/cypher的角度来看,这确实是你想要做的...
match (u:User {id: 1})-[r:FOLLOWS]->(p:Publisher)
with u, p, r
match p-[:PUBLISHED]-(i:Item)
with u, p, r, i
order by i.name
return u.name, p.name, i.name
skip 5
limit 2
您只需要参数化最小值和最大值并在 运行 时绑定它们,这将涉及两个查询而不是一个,但我认为这仍然是一个优雅的解决方案。我曾尝试在 skip
和 limit
中包含属性,但 cypher 显然不支持(目前)。它需要一个参数或一个无符号整数。
match (u:User {id: 1})-[r:FOLLOWS]->(p:Publisher)
with u, p, r
match p-[:PUBLISHED]-(i:Item)
with u, p, r, i
order by i.name
return u.name, p.name, i.name
skip {min}
limit {max}
Let's say I want to have 2-5 posts from you everyday, if you post that much. If you post only one, it is alright, and I will have the only one post.
你的解释min
号码将是无效的噪音。
这不是你写的,但这是有道理的:
给定 post 的最大显示槽(外层 LIMIT
),我想得到 min
post 首先来自每个朋友(如果有的话)。如果在那之后还有空位,每个朋友最多填max
posts。
在示例中,来自朋友 2 的 1 (min
) post 具有最高优先级,另外 2 (max - min
) posts 如果有更多插槽仍然可用。
如果每个优先级都没有足够的空位,那么 post 可以任意选择。我继续假设每个人的第一个 post 应该首先 selected,等等
剩下的仍然是随意的,但是如果您设法制定需求就可以轻松解决。
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
注释
假设
friends.user_id
和posts.id
为主键。那里缺少您的 table 定义。应定义所有其他列
NOT NULL
以使其有意义。仅使用
LATERAL
加入 selectmax
postings per friend in the subquery:在子查询中使用
row_number()
, notrank()
。混淆两者是一个常见的错误。您提到了
date
但它没有显示在您的查询中。也许你真的想要:, row_number() OVER (ORDER BY rating DESC NULLS LAST , date DESC NULLS LAST) AS rn
DESC NULLS LAST
只是因为rating
和date
可能为 NULL:- PostgreSQL sort by datetime asc, null first?
在 Postgres 中,您可以在
中使用简单的布尔表达式ORDER BY
:ORDER BY (p.rn > f.min), p.rn
- SQL select query order by day and month
- Sorting null values after all others, except special
每个朋友的
min
post 放在第一位。第二项(p.rn
)给每个朋友一个平等的机会(第一个post第一个等等)。不要使用
作为标识符。它是标准中的保留字 SQL 和 Postgres 中的基本类型名称。date