简体   繁体   中英

SQL - Compare two sets

Lets say, I have employees, and I know what fruits they like.

fruits(name, fruit_name)

My question is: list all the employees which at least like the same fruits as Donald.

So how do I compare two set of values?

This is how I get the fruits which Donald likes:

Select name, fruit_name
from fruits
where initcap(name) like '%Donald%';

Example: Donald likes apples, pears, peaches. I need the people who like apples, pears, peaches and possibly other fruits, but they must like those 3.

You can use self join to get your desired result- I have tweaked your query a little to get the output-

select distinct e1.name from fruits e1,(Select name, fruit_name
from fruits
where initcap(name) like '%Donald%') e2
where e1.fruit_name = e2.fruit_name;

the above query returns employees for whom atleast one fruit matches with Donald

Below tested Query gives employees for whom atleast all the Donald's fruits matches

     select name from (
    select name,count(1) cnt  from
    (select name,fruit_name, case when fruit_name in (Select distinct fruit_name
        from fruits
        where initcap(name) like '%Donald%') then 1 else 0 end fruit_match from fruits)
    where fruit_match = 1 group by name) where  cnt >=
 (select count(distinct fruit_name) from fruits where initcap(name) like '%Donald%');

Two ways to do this:

Using Collections

I find this gives the most comprehensible SQL but it does require defining a collection type:

CREATE TYPE VARCHAR2s_Table AS TABLE OF VARCHAR2(20);

Then you can just group everything up into collections and use a self join and SUBMULTISET OF to find the other names.

WITH grouped AS (
  SELECT name,
         CAST( COLLECT( fruit ) AS VARCHAR2s_Table ) AS list_of_fruits
  FROM   fruits
  GROUP BY name
)
SELECT g.name
FROM   grouped f
       INNER JOIN
       grouped g
       ON (     f.list_of_fruits SUBMULTISET OF g.list_of_fruits
            AND f.name <> g.name )
WHERE  f.name = 'Alice';

SQLFIDDLE

Or an alternative version of this:

WITH grouped AS (
  SELECT name,
        CAST( COLLECT( fruit ) AS VARCHAR2s_Table ) AS list_of_fruits
  FROM fruits
  GROUP BY name
)
SELECT name
FROM   grouped
WHERE  name <> 'Alice'
AND    ( SELECT list_of_fruits FROM grouped WHERE name = 'Alice' )
       SUBMULTISET OF list_of_fruits ;

Not using Collections

WITH match_by_user AS (
  SELECT DISTINCT
         name,
         fruit
  FROM   fruits
  WHERE  name = 'Alice'
)
SELECT f.name
FROM   fruits f
       INNER JOIN
       match_by_user m
       ON (     f.fruit = m.fruit
            AND f.name  <> m.name )
GROUP BY f.name
HAVING COUNT( DISTINCT f.fruit ) = ( SELECT COUNT(1) FROM match_by_user );

SQLFIDDLE

As an aside - using INITCAP(Name) LIKE '%Name%' has the potential to match multiple names and you might find that you are finding the fruits that one of several people like.

SELECT DISTINCT emp.name
FROM fruits emp
JOIN fruits don ON (don.fruit_name = emp.fruit_name)
WHERE INITCAP(fruits.name) LIKE '%Donald%';

This gets you the names of all employees that have at least one fruit like in common with Donald, although you should note that your solution to getting all fruits Donald likes, also gives you the fruits that say, Ronald McDonald likes.


I see you need all people who like at least all fruits Donald likes. This is harder.

SELECT DISTINCT emp.name
FROM employees emp
WHERE NOT EXISTS (
    SELECT *
    FROM fruits don_fruits
    LEFT JOIN fruits emp_fruits
        ON (don_fruits.fruit_name = emp_fruits.fruit_name AND emp_fruits.name = emp.name)
    WHERE INITCAP(don_fruits.name) LIKE '%Donald%'
      AND emp_fruits.name IS NULL
);

In oracle plsql, I would use something like this to print all employees that like all fruits that donald likes :

declare 
emp_name fruits.name%type;

begin

for rec in select distinct name from fruits
 loop

select name into emp_name from
(
 Select name, fruit_name
  from fruits
   where fruits.name=rec.name
 minus
   Select name, fruit_name
  from fruits
 where initcap(name) like '%Donald%'
)

if emp_name is null
 then
 dbms_output.put_line('Employee' || rec.name || 'likes same fruits as Donald');
end if

 end loop 

end

Try this:

Select distinct f2.name 
from fruits f1, fruits f2 where f1.fruit_name = f2_fruit_name 
where initcap(f1.name) like '%Donald%';
select name
  from fruits
 where fruit_name in (select fruit_name
                        from fruits
                       where initcap(name) like '%Donald%'
                     )
   and initcap(name) not like '%Donald%'
 group by name
having count(fruit_name) = (select count(fruit_name)
                              from fruits
                             where initcap(name) like '%Donald%'
                            );

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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