sql - 只返回符合所有条件的行

标签 sql postgresql

这是一个粗略的架构:

create table images (
    image_id serial primary key,
    user_id int references users(user_id),
    date_created timestamp with time zone
);

create table images_tags (
    images_tag_id serial primary key,
    image_id int references images(image_id),
    tag_id int references tags(tag_id)       
);

输出应该是这样的:

{"images":[
    {"image_id":1, "tag_ids":[1, 2, 3]},
    ....
]}

允许用户根据用户 ID、标签和偏移量过滤图像 image_id .例如,有人可以说 "user_id":1, "tags":[1, 2], "offset_image_id":500 ,这将为他们提供来自 user_id 的所有图像1,同时具有标签 1 和 2,以及一个 image_id 500 或更少。

棘手的部分是“同时拥有标签 1 和 2”。返回所有具有 1、2 或两者的图像更直接(也更快)。除了聚合之外,我看不到任何解决方法,但它要慢得多。

有什么帮助可以快速做到这一点吗?

这是我正在使用的当前查询,它非常慢:

select * from (
    select i.*,u.handle,array_agg(t.tag_id) as tag_ids, array_agg(tag.name) as tag_names from (
        select i.image_id, i.user_id, i.description, i.url, i.date_created from images i
        where (?=-1 or i.user_id=?)
        and (?=-1 or i.image_id <= ?)
        and exists(
            select 1 from image_tags t
            where t.image_id=i.image_id
            and (?=-1 or user_id=?)
            and (?=-1 or t.tag_id in (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?))
        )
        order by i.image_id desc
    ) i
    left join image_tags t on t.image_id=i.image_id
    left join tag using (tag_id) --not totally necessary
    left join users u on i.user_id=u.user_id --not totally necessary
    group by i.image_id,i.user_id,i.description,i.url,i.date_created,u.handle) sub
where (?=-1 or sub.tag_ids @> ?)
limit 100;

最佳答案

当这条语句的执行计划确定后,在准备时,PostgresSQL 计划器不知道这些 ?=-1 条件中的哪一个为真或不为真。

所以它必须制定一个计划,可能过滤特定的 user_id,也可能不过滤,可能过滤 image_id 的范围,也可能不过滤,也许过滤一组特定的 tag_id,也可能不过滤。这可能是一个愚蠢的、未优化的计划,无法利用索引。

虽然您当前的覆盖所有情况的大型通用查询策略对于正确性来说是可以的,但为了性能,您可能需要放弃它以支持或生成最小查询给定实际填充的参数化条件。

在这样生成的查询中,?=-1 or ... 将消失,只有实际需要的连接才会出现,可疑的 t.tag_id in ( ?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) 将消失或减少到绝对必要的程度。

如果给定某些参数集仍然很慢,那么您将有一个更容易优化的起点。


至于问题的要点,测试所有标签的完全匹配,您可能想在内部子查询中尝试惯用形式:

SELECT image_id FROM image_tags
  WHERE tag_id in (?,?,...)
  GROUP BY image_id HAVING count(*)=?

最后一个 ? 是作为参数传递的标签数量。

(并完全删除 sub.tag_ids @> ? 作为外部条件)。

关于sql - 只返回符合所有条件的行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23778632/

相关文章:

php - 如何在数据库中正确插入浮点变量

MySQL:列数与第 1 行的值数不匹配

sql - 相当于 Oracle XE 11.2 中 PostgreSQL 的 array_agg

没有递归的sql递归

php - 在循环中使用 PHP/Postgresql 查询值分配 JavaScript 数组

mysql - 分组并计数不返回我期望的

java - 用 Java 程序编写的 SQL 查询

sql - 如何在sql server中创建依赖于其他列的默认约束

sql - 将数组从 jsonb_array_elements_text() 转换为整数数组

ruby-on-rails - 我怎样才能让列自动计算自己? rails