我有这个people
和tags
桌子,像这样,
CREATE TABLE people
AS
SELECT *
FROM ( VALUES
(1,'Joe'),
(2,'Jane')
) AS t(id,name);
CREATE TABLE tags
AS
SELECT * FROM ( VALUES
(1, 1, 'np'),
(2, 1, 'yw'),
(3, 2, 'np')
) AS t(id, people_id, tag);
如果我想使用连接在表中查找同时包含 和 标签的所有内容people
,我将如何在 Postgres 9.6 中高效地执行此操作?np
yw
tags
people
在这种情况下,我应该只从表中获取 Joe 的记录。
以下是一些不涉及使用
array_agg
.针对每个标签返回
INTERSECT
的集合使用运算符:people_id
或者您可以使用 a
COUNT(DISTINCT tag) = 2
来查找具有这两个标签的人。(请注意,DISTINCT
添加 是为了处理一个人可能有两次相同标签的情况。如果不可能,删除是安全的。)第二种方法更容易扩展以接受任意数量的标签,尽管第一种方法并非不可能。
还有两种方法 - 使用连接或相关子查询 - 没有
GROUP BY
:第一个使用
EXISTS
子查询:第二个假设对 的
UNIQUE
约束(tag, people_id)
:在dbfiddle.uk进行测试。
还要在 SO 上检查这个问题,有 10 多种方法可以解决此类问题 - 以及性能分析: How to filter SQL results in a has-many-through relation。
他们甚至有一个标签:关系划分
这可能令人惊讶,但大多数情况下,many join 方法、many exists 和类似的方法(如使用的方法
INTERSECT
)比方法更有效group by / count
。但当然有许多细节对性能很重要。查询参数、表大小、索引、数据分布等等都会影响各种方法的性能。在这里,我们选择所有人并对标签进行数组聚合。我们一次性完成。然后我们将其包装在一个子选择中,并找到同时具有
np
, 和的所有匹配项yw
。您有时可以通过降低条件来加快速度
您也可以直接将标签数组放在上面
people
。然后查询它变得非常简单。mendosi's answer 的一个小变体,它避免了
WITH
:这种方法与他/她的方法有一些小的区别:
WITH
语句(很久以前就不是 PostgreSQL 的情况)WITH
WITH
优化栅栏;并且(从今天开始)最终阻止数据库执行一些优化。dbfiddle在这里
如果您正在寻找最快的解决方案,我会在实际条件下检查不同的方法,并根据您实际获得的时间来决定。我提出的查询非常标准,应该不会比带有 a 的查询慢
WITH
,但它是否比其他方法慢或快,我事先并不知道。另一种简单的等值连接方式:
dbfiddle在这里