【发布时间】: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 专家,但我想我希望的是
- 一种在 LINQ 中模仿上述愚蠢废话的方法
- 另一种更优雅的方法可以让我做我需要的并且适用于任何 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 @> ('Tag1', 'Tag2')。这也是一个可行的选择。它进行聚合而不是过度连接。虽然不确定 LINQ 查询会是什么样子
【问题讨论】:
-
tag_list=['demo', 'test']Book.objects.filter(tags__tag__in=tag_list)也许是这个? -
@HemalPatel 我认为这会找到标签是演示或测试的书
-
@TrueGopnik 好的,他想要带有过滤器中所有标签的书,对吗?
__in不会工作是的 -
是的......“任何一个”都很简单。 “全部”是问题..“只有这些”会更烦人
-
带有
__in谓词和count聚合的标记子查询由= 2(搜索的标记数)在外部过滤