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上的条件,以仅添加符合条件的标记(如果适用)。噢,谢谢!现在它更有意义了;我只是在查找子关键字的意思,呵呵。让我试试这个!