【问题标题】:SQL query to filter on group of related rowsSQL 查询以过滤相关行组
【发布时间】:2022-11-24 07:04:22
【问题描述】:

我在 SQL 查询中有一个反复出现的问题,我无法优雅地解决,无论是在原始 SQL 还是 Django ORM 中,现在我在 EntityFramework 中也遇到了它。它可能很常见,有自己的名字,但我不知道。

说,我在两个表之间有一个简单的外键关系,例如

Book 1 <- * Tag

一本书有很多标签,一个标签有一本书,即 Tag 表有书表的外键。

现在,我想找到所有有“Tag1”和“Tag2”的书。

原始 SQL

我可以进行多个连接

SELECT * FROM books
JOIN tags t1 on tags.book_id = books.id
JOIN tags t2 on tags.book_id = books.id
WHERE t1.tag = 'Tag1' AND t2.tag = 'Tag2'

很酷,可行,但似乎性能不佳

姜戈

在 Django 中,我可以做类似的事情

Book.objects.filter(tags__tag="Tag1").filter(tags__tag="Tag1")

像这样更改过滤器会导致额外的连接,就像在原始 SQL 版本中一样

实体框架 LINQ

我尝试链接 .Where() 类似于更改 Django 的 .filter(),但结果不同。它将构建一个类似于以下内容的查询,当然不会返回任何内容,因为没有行的标记是两个不同的字符串

SELECT * FROM books
JOIN tags t1 on tags.book_id = books.id
WHERE t1.tag = 'Tag1' AND t1.tag = 'Tag2'

总结起来

我想我可以做一个数组聚合来将标签聚合到数组中并与之进行比较,但这似乎也很昂贵,聚合和分组也会影响事物的排序,这迫使我进行子查询以获得我想要的顺序.

正如您清楚地看到的那样,我绝不是 SQL 专家,但我想我希望的是

  1. 一种在 LINQ 中模仿上述愚蠢废话的方法
  2. 另一种更优雅的方法可以让我做我需要的并且适用于任何 ORM

    额外的杂谈

    在这种情况下,我需要找到具有“所有”标签列表的书籍,这是一个棘手的问题……如果它是“任何一个”或“这个特定的”,那么它就很简单了。

    编辑:使用数组和重叠的解决方案

    在 Postgres 中,我们可以通过 array_agg 将所有相关标签聚合到一个数组中,如下所示:

    SELECT * FROM books
    JOIN tags t1 on tags.book_id = books.id
    ;
    
    +--------+-------+------+
    | BookId | Name  | Tag  |
    +--------+-------+------+
    |      1 | BookA | Tag1 |
    |      1 | BookA | Tag2 |
    |      1 | BookA | Tag3 |
    |      2 | BookB | Tag1 |
    |      2 | BookB | Tag3 |
    +--------+-------+------+
    
    
    SELECT books.BookId, Name, array_agg(t1.tags) as tags
    FROM books
    JOIN tags t1 on tags.book_id = books.id
    GROUP BY BookId
    ORDER BY BookId
    ;
    
    +--------+-------+--------------------+
    | BookId | Name  |        tags        |
    +--------+-------+--------------------+
    |      1 | BookA | [Tag1, Tag2, Tag3} |
    |      2 | BookB | {Tag1, Tag3}       |
    +--------+-------+--------------------+
    

    这样,我就可以使用数组“包含”运算符来查找 tag 与预期集合重叠的行:WHERE tags @&gt; ('Tag1', 'Tag2')

    这也是一个可行的选择。它进行聚合而不是过度连接。虽然不确定 LINQ 查询会是什么样子

【问题讨论】:

  • tag_list=['demo', 'test'] Book.objects.filter(tags__tag__in=tag_list) 也许是这个?
  • @HemalPatel 我认为这会找到标签是演示或测试的书
  • @TrueGopnik 好的,他想要带有过滤器中所有标签的书,对吗? __in不会工作是的
  • 是的......“任何一个”都很简单。 “全部”是问题..“只有这些”会更烦人
  • 带有 __in 谓词和 count 聚合的标记子查询由 = 2(搜索的标记数)在外部过滤

标签: sql django linq


【解决方案1】:

使用group byhaving,我们可以做Tag 1Tag 2

with cte_tags as (
select book_id
  from tags
 where tag in ('Tag 1', 'Tag 2')
 group by book_id
 having count(*)=2)
select b.id as book_id,
       b.name
  from books b
  join cte_tags t
    on b.id = t.book_id;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-22
    • 1970-01-01
    • 1970-01-01
    • 2014-08-31
    • 2017-05-03
    • 2020-03-08
    • 1970-01-01
    相关资源
    最近更新 更多