2016-03-18 37 views
3

http://sqlfiddle.com/#!4/bab93d甲骨文返回行只有当所有的连接条件匹配

参见SQL小提琴例子......我有客户,标签和映射表。我试图通过标签实现客户搜索,并且它必须是AND搜索。查询被传递一个标签标识符列表(任何数字),并且它只能返回具有所有标签的客户。

在这个例子中,我使用了一个IN运算符,但是这对应于OR搜索并且不能解决我的问题。查询看起来应该是什么样的AND搜索?

select 
    * 
from 
    customer c 
    inner join customer_tag ct on ct.customer_id = c.customer_id 
where 
    ct.tag_id in (1, 2); 

这两者都返回客户,但只有第一个客户被打上标签1和2

+0

你知道事先有多少个标签吗?如果你知道你必须匹配X标签,你可以将它添加到你的逻辑 –

+0

那么,用户选择标签,所以查询本身不知道。但它可以计算有多少人通过它。 – Edgar

回答

3

你可以使用相关子查询来获取所有客户的名单:

SELECT * 
FROM customer c 
WHERE c.customer_ID IN 
(
    SELECT customer_id 
    FROM customer_tag ct 
    WHERE ct.customer_id = c.customer_id 
     AND ct.tag_id IN (1,2) 
    GROUP BY customer_id 
    HAVING COUNT(DISTINCT tag_id) = 2 
); 

LiveDemo

很容易扩展ju ST:

WHERE ct_tag IN (1,2,3) 
... 
HAVING COUNT(DISTINCT tag_id) = 3 
+0

所有的答案解决了这个问题,谢谢你们:)接受这一个,因为它是第一个。 – Edgar

1

你可以做类似

SELECT * 
FROM customer c 
WHERE 
    2 = (SELECT COUNT(DISTINCT ct.tag_id) 
     FROM customer_tag ct 
     WHERE ct.customer_id = c.customer_id AND ct.tag_id IN (1, 2)); 

2 = ...部分应根据您要搜索的标签数量进行调整。


替代的解决方案 - 相关子查询可以有表现不佳,所以你可以在表中加入这样

SELECT c.* 
FROM customer c 
    INNER JOIN (
    SELECT ct.customer_id 
    FROM customer_tag ct 
    WHERE ct.tag_id IN (1, 2) 
    GROUP BY ct.customer_id 
    HAVING COUNT(DISTINCT ct.tag_id) = 2 
) ct ON ct.customer_id = c.customer_id; 
+0

@Edgar我添加了一个不使用相关子查询的更快解决方案。如果你想检查出来。 – radoh

+0

感谢您的额外解决方案。 – Edgar

+1

为什么不是常规的'INNER JOIN'?然后你可以跳过'WHERE'子句。 - 就像我的回答! – jarlh

3

JOIN版本:

SELECT c.* 
FROM customer c 
JOIN (SELECT customer_id 
     FROM customer_tag 
     WHERE tag_id IN (1,2) 
     GROUP BY customer_id 
     HAVING MAX(tag_id) <> MIN(tag_id)) ct ON c.customer_id = ct.customer_id 

如果你有2个以上不同值,请使用COUNT DISTINCT代替,如下所示:

SELECT c.* 
FROM customer c 
JOIN (SELECT customer_id 
     FROM customer_tag 
     WHERE tag_id IN (1,2,3) 
     GROUP BY customer_id 
     HAVING COUNT(DISTINCT tag_id) = 3) ct ON c.customer_id = ct.customer_id 
+1

我同意这个解决方案。但是如何为'IN(1,2,3)'这样的更多值生成'having'子句呢? – lad2025

+0

所以它基本上就像我的版本。只需将'IN'改为'JOIN' :) – lad2025

+0

@ lad2025,几乎相同,但我有一个JOIN,可能会进行不同的优化(取决于dbms)。 – jarlh

1

扩展其他的答案,我会添加一个版本,你不必指望标签也列出了他们两次:

WITH tgs as(
    select distinct tag_id 
    from Tags 
    where tag_id in (1, 2, 3, 4) --modify only here 
) 
SELECT c.* 
FROM customer c 
JOIN (SELECT customer_id 
     FROM customer_tag 
     WHERE tag_id in (select tag_id from tgs) 
     GROUP BY customer_id 
     HAVING COUNT(DISTINCT tag_id) = (select count(*) from tgs) 
    ) ct ON c.customer_id = ct.customer_id 

如果表是正确标准化(并有PKS等),你可以删除distinct关键字。

+0

酷,这么长时间没有写SQL了,我忘了'WITH'。 – Edgar