使 IN 子句表现得像 AND 子句

Dim*_*ets 2 postgresql relational-division

我在stackoverlow上问过同样的问题,但没有得到答案,所以这就是我在这里问的原因(我读过应该在 SO 上问简单的 sql 问题,但我别无选择)。

假设我有 3 个表:posts,post_categoriescategories. 我正在实现某种带有过滤器的页面,我可以在其中按类别过滤帖子。用户可以选择多个类别。当他选择多个时,它们应该相加。我无法让它工作。我现在拥有的是简单的IN()SQL 子句:

SELECT posts.id, post_categories.category_id FROM posts 
JOIN post_categories ON posts.id = post_categories.post_id 
WHERE post_categories.category_id IN (1,2,3) LIMIT 10;
Run Code Online (Sandbox Code Playgroud)

但它并不匹配所有的 id,它是OR,我需要AND. 我在这里需要的是查找类别为 的所有帖子id=1 AND id=2 AND id=3,但此查询返回IN()列表中至少有一个类别的帖子。

Cra*_*ger 5

IN就像写作= ANY,例如

regress=> SELECT 1 = ANY (ARRAY[1, 2, 3]);
 ?column? 
----------
 t
(1 row)
Run Code Online (Sandbox Code Playgroud)

对您的问题的简单解释是说您要求的是= ALL,但这很少有意义:

regress=> SELECT 1 = ALL (ARRAY[1, 2, 3]);
 ?column? 
----------
 f
(1 row)
Run Code Online (Sandbox Code Playgroud)

你有什么真正想要的找到所有posts有相应post_categories的每一家上市集的条目category_id

与关系数据库中的惯例一样,只要您清楚地构建了问题,解决方案就会开始自行编写。

您可以执行多重连接,就像 Daniel 建议的那样,但这很痛苦,因为它需要动态 SQL。或者您可以使用关系集操作。

相反,我可能会使用 PostgreSQL 的数组功能。如果您提供了示例数据,这会更容易,但我认为您需要类似以下内容,它会找到与帖子匹配的每个类别,然后检查所有类别是否匹配(HAVING子句):

SELECT
   posts.id
FROM posts 
INNER JOIN post_categories ON posts.id = post_categories.post_id 
WHERE post_categories.category_id IN (1,2,3)
GROUP BY posts.id
HAVING array_agg(post_categories.category_id) @> ARRAY[1,2,3]
LIMIT 10
Run Code Online (Sandbox Code Playgroud)

未经测试,因为您没有以CREATE TABLEINSERTs 形式提供示例数据,但这是总体思路。@>表示“数组包含”;我怀疑它会比使用array_agg(category_id ORDER BY category_id)和对排序ARRAY文字进行相等测试更有效,因为您避免了聚合排序。

您可以省略该WHERE子句,这仍然有效,但它可能会很慢,因为它无法过滤掉在进行聚合之前没有任何类别匹配的帖子。

顺便说一句,LIMIT没有ORDER BY通常是一个错误。数据库可以按任何顺序返回任何一组 10 个匹配结果。