选择每个子行都满足条件的行

时间:2018-06-21 11:46:03

标签: sql oracle

在我的Oracle数据库中,我有两个表一对多的关系:经理和雇员。

+------------+-------+------------+
| Manager_ID | Name  | Department |
+------------+-------+------------+
|          1 | Steve | Sales      |
|          2 | Ben   | Sales      |
|          3 | Molly | Accounts   |
+------------+-------+------------+

+-------------+------------+--------+-----+
| Employee_ID | Manager_ID |  Name  | Age |
+-------------+------------+--------+-----+
|           1 |          1 | Kyle   |  25 |
|           2 |          1 | Gary   |  31 |
|           3 |          2 | Renee  |  31 |
|           4 |          2 | Oliver |  32 |
+-------------+------------+--------+-----+

我该如何只选择每个员工都超过30岁的经理?

在我的示例数据中,满足此条件的唯一经理是Ben,因为他的两个雇员都超过30岁。

我认为可以这样做,但这是错误的:

SELECT m.manager_id 
FROM   managers m 
WHERE  m.manager_id IN (SELECT e.manager_id 
                        FROM   employees e 
                        GROUP  BY e.manager_id 
                        HAVING e.age > 30) 

6 个答案:

答案 0 :(得分:2)

使用not exists

select m.*
from manager m
where not exists (select 1 
                  from Employees e 
                  where e.Manager_ID = m.Manager_ID and e.Age < 30
                 ) and
      exists (select 1 from Employees e where e.Manager_ID = m.Manager_ID)

答案 1 :(得分:2)

我不喜欢Yogesh的答案(我赞成,因为它可能是我写的方式,我对此表示反对)是必须第二次进入employees表确保经理实际上至少有一名员工。

从好的方面来说,Yogesh所使用的NOT EXISTS将使Oracle一旦发现经理太年轻,就可以停止寻找经理的雇员。所以,也许是一个折腾。

我将提供这种选择。它比NOT EXISTS短,并且不必第二次进入employees表。

SELECT m.*
FROM  manager m 
CROSS APPLY ( 
   SELECT min(age) min_age 
   FROM employee e 
   WHERE e.manager_id = m.manager_id ) ma
where ma.min_age >= 30;

答案 2 :(得分:1)

SELECT manager_id 
FROM   employees -- managers
minus
select manager_id
from   employees
where  age <= 30

答案 3 :(得分:1)

使用子查询进行计数

SQL> WITH manager(Manager_ID, Name, Department) AS (
  2    SELECT 1,  'Steve', 'Sales' FROM dual UNION ALL
  3    SELECT 2, 'Ben', 'Sales' FROM dual UNION ALL
  4    SELECT 3, 'Molly', 'Accounts' FROM dual),
  5  employee(Employee_ID, Manager_ID, Name, Age) AS (
  6    SELECT 1 , 1, 'Kyle', 25 FROM dual UNION ALL
  7    SELECT 2 ,1, 'Gary', 31  FROM dual UNION ALL
  8    SELECT  3, 2, 'Renee', 31  FROM dual UNION ALL
  9    SELECT 4, 2 , 'Oliver', 32  FROM dual)
 10  ---------------------------
 11  --- End of data preparation
 12  ---------------------------
 13  SELECT m.name
 14    FROM manager m
 15    JOIN (SELECT manager_id,
 16                 COUNT(1) total,
 17                 COUNT(CASE WHEN age > 30 THEN 1 ELSE NULL END) age_30_above
 18            FROM employee
 19           GROUP BY manager_id) ee
 20      ON m.manager_id = ee.manager_id
 21   WHERE total = age_30_above;

输出

NAME
-----
Ben

您的查询将是:

SELECT m.name
  FROM manager m
  JOIN (SELECT manager_id,
               COUNT(1) total,
               COUNT(CASE WHEN age > 30 THEN 1 ELSE NULL END) age_30_above
          FROM employee
         GROUP BY manager_id) ee
    ON m.manager_id = ee.manager_id
 WHERE total = age_30_above;

答案 4 :(得分:0)

您可以像这样使用ALL function

SELECT m.manager_id 
  FROM managers m
 WHERE (30 <= ALL (SELECT e.age FROM employees e WHERE e.manager_id = m.manager_id));

答案 5 :(得分:0)

您可能想取消条件,选择所有没有30岁以下员工的经理

select * from managers
where manager_id not in (select manager_id
                         from employees
                         where age < 30)