3

以下是我的查询:

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

它搜索我朋友的帖子,按他们的评分和日期排序。MAX此查询中实现的窗口函数让我可以根据表上的字段限制为每个朋友返回的行数Friends

但是,我还有一个字段MIN,用于指定我希望从给定朋友的查询中获得的最小帖子数。这怎么可能?

我还想知道 SQL 是否是这些类型查询的最佳选择?我已经尝试过 Neo4j Graph 数据库,虽然它似乎是一个很好的解决方案,但我宁愿避免使用 2 个单独的数据库。

SQLFiddle

架构:

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)具有以下条件的组合:

  1. id来自=2的朋友的 1 到 3 个帖子
  2. id来自=3的朋友的 0 到 5 个帖子
  3. id来自=4的朋友的 2 到 10 个帖子

所以基本上,如果我的朋友friend_id=2发表了 1 篇或更多篇文章,我至少想要 2 篇。如果他发布的文章超过 3 篇,我希望不超过 3 篇。

4

3 回答 3

5

假设我想每天收到你 2-5 篇文章,如果你发了那么多。如果你只发一篇也没关系,我就只发一篇。

在评论中的解释仍然没有加起来。min根据这个解释,你的号码将是没有效果的噪音。

这不是你写的,但这是有道理的:

给定帖子的最大显示槽(外部LIMIT),我想min先从每个朋友那里获取帖子(如果有的话)。如果之后有空位,请为每个朋友填写最多max帖子。

在示例中,朋友 2 的 1 ( ) 个帖子具有最高优先级,如果还有更多空位可用,则min另外 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

SQL小提琴。

笔记

于 2015-05-03T10:59:53.933 回答
0

我错过了什么吗?你不能只是添加and subq.rnk >= subq.minwhere条款吗?

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
于 2015-05-02T11:17:00.733 回答
0

我认为从 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

您只需要参数化最小值和最大值并在运行时绑定它们,这将涉及两个查询而不是一个,但我认为这仍然是一个优雅的解决方案。我曾尝试在其中skiplimit之前包含属性,但 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}
于 2015-05-02T12:36:46.203 回答