简体   繁体   English

仅当所有连接条件都匹配时,Oracle才会返回行

[英]Oracle return rows only if all join conditions match

http://sqlfiddle.com/#!4/bab93d http://sqlfiddle.com/#!4/bab93d

See the SQL Fiddle example... I have Customers, Tags, and a mapping table. 请参阅SQL小提琴示例...我有客户,标签和映射表。 I am trying to implement customer search by tags, and it has to be an AND search. 我试图通过标签实现客户搜索,它必须是AND搜索。 The query is passed a list of tag identifiers (any number), and it has to return only customers that have ALL the tags. 查询将传递一个标记标识符列表(任意数字),并且只能返回包含所有标记的客户。

In the example I have used an IN operator, but that corresponds to an OR search and doesn't solve my problem. 在示例中,我使用了一个IN运算符,但它对应于OR搜索并且不能解决我的问题。 What should the query look like to be an AND search? 查询应该是什么样的AND搜索?

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

This returns both customers, but only the first customer is tagged with tag 1 and 2. 这将返回两个客户,但只有第一个客户标记有标记1和2。

You could use correlated subquery to get list of all customers: 您可以使用相关子查询来获取所有客户的列表:

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

It is easy to extend just: 它很容易扩展:

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

JOIN version: 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

If you have more than 2 different values, use COUNT DISTINCT instead, like this: 如果您有两个以上的不同值,请使用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

You could do something like 你可以做点什么

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));

The 2 = ... part should be adjusted according to the number of tags you are trying to search for. 2 = ...部分应根据您尝试搜索的标签数量进行调整。


Alternative solution - correlated subqueries can have poor performance, so you could join in a table like this 替代解决方案 - 相关子查询可能性能较差,因此您可以加入这样的表

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;

Extending the other answers, I will add a version where you don't need to count the tags nor list them twice: 扩展其他答案,我将添加一个版本,您不需要计算标签,也不需要列出两次:

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

If the tables are rightly normalized(and have pks, etc) you can remove the distinct keywords. 如果表格正确规范化(并具有pks等),则可以删除distinct关键字。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM