Java 基于不同主键进行限制时的一对多查询

Java 基于不同主键进行限制时的一对多查询,java,sql,postgresql,left-join,sql-limit,Java,Sql,Postgresql,Left Join,Sql Limit,我有一张这样的桌子: create table images ( image_id serial primary key, user_id int references users(user_id), date_created timestamp with time zone ); select image_id,user_id,tag_id from images left join images_tags using(image_id) where (?=-1 or

我有一张这样的桌子:

create table images (
    image_id serial primary key,
    user_id int references users(user_id),
    date_created timestamp with time zone
);
select image_id,user_id,tag_id from images left join images_tags using(image_id)
where (?=-1 or user_id=?)
and (?=-1 or tag_id in (?, ?, ?, ?)) --have up to 4 tag_ids to search for
order by date_created desc limit 100;
{"images":[
    {"image_id":1, "tag_ids":[1, 2, 3]},
    ....
]}
然后,我有一个标记表,用于图像可以具有的标记:

create table images_tags (
    images_tag_id serial primary key,
    image_id int references images(image_id),
    tag_id int references tags(tag_id)       
);
为了得到我想要的结果,我运行如下查询:

create table images (
    image_id serial primary key,
    user_id int references users(user_id),
    date_created timestamp with time zone
);
select image_id,user_id,tag_id from images left join images_tags using(image_id)
where (?=-1 or user_id=?)
and (?=-1 or tag_id in (?, ?, ?, ?)) --have up to 4 tag_ids to search for
order by date_created desc limit 100;
{"images":[
    {"image_id":1, "tag_ids":[1, 2, 3]},
    ....
]}
问题是,我想根据唯一映像ID的数量进行限制,因为我的输出如下所示:

create table images (
    image_id serial primary key,
    user_id int references users(user_id),
    date_created timestamp with time zone
);
select image_id,user_id,tag_id from images left join images_tags using(image_id)
where (?=-1 or user_id=?)
and (?=-1 or tag_id in (?, ?, ?, ?)) --have up to 4 tag_ids to search for
order by date_created desc limit 100;
{"images":[
    {"image_id":1, "tag_ids":[1, 2, 3]},
    ....
]}
注意我是如何将标记id分组到一个数组中进行输出的,即使SQL为每个标记id和图像id组合返回一行


所以,当我说限制100时,我希望它适用于100个唯一的图像ID。

也许你应该在每一行上放一个图像?如果有效,您可以执行以下操作:

select image_id, user_id, string_agg(cast(tag_id as varchar(2000)), ',') as tags
from images left join
     images_tags
     using (image_id)
where (?=-1 or user_id=?) and
      (?=-1 or tag_id in (?, ?, ?, ?)) --have up to 4 tag_ids to search for
group by image_id, user_id
order by date_created desc
limit 100;
如果不起作用,则使用CTE:

with cte as (
      select image_id, user_id, tag_id,
             dense_rank() over (order by date_created desc) as seqnum
      from images left join
           images_tags
           using (image_id)
      where (?=-1 or user_id=?) and
            (?=-1 or tag_id in (?, ?, ?, ?)) --have up to 4 tag_ids to search for
    )
select *
from cte
where seqnum <= 100
order by seqnum;

首先选择100个符合条件的图像,然后加入图像\u标记。 使用一个来满足images_标记上的条件,并注意正确使用括号

SELECT i.*, t.tag_id
FROM  (
   SELECT i.image_id, i.user_id
   FROM   images i
   WHERE (? = -1 OR i.user_id = ?)
   AND   (? = -1 OR EXISTS (
      SELECT 1
      FROM   images_tags t
      WHERE  t.image_id = i.image_id
      AND    t.tag_id IN (?, ?, ?, ?)
      ))
   ORDER  BY i.date_created DESC
   LIMIT  100
   ) i
LEFT   JOIN images_tags t
            ON t.image_id = i.image_id
           AND (? = -1 OR t.tag_id in (?, ?, ?, ?)) -- repeat condition
这应该比使用窗口函数和CTE的解决方案更快。
使用测试工具测试性能。正如往常一样,运行几次来预热缓存。

+1,不过有一个小问题是,您需要将标记id从int转换为字符类型,以便它在string\u agg中工作。因为它无论如何都要变成一个数组,所以array\u agg可能更合适。事实上,我也可以使用array\u agg。实际上,我以前不知道这些聚合函数。谢谢这很有道理,但是我在select子句中得到一个错误,说我缺少表i的from子句。@tau:对不起,输入错误。用表别名sub代替i。另外,您需要在最后一次加入时重复t上的条件,以仅添加符合条件的标记(如果适用)。噢,谢谢!现在它更有意义了;我只是在查找子关键字的意思,呵呵。让我试试这个!