Based on the SQLD exam syllabusReport an error
=, >, < with a single-row subquery, and IN, ANY, ALL, EXISTS with a multi-row subquery. Mixing them up gives ORA-01427.> ANY means "greater than the minimum", and > ALL means "greater than the maximum". When the subquery returns no rows, ALL is true and ANY is false.NOT IN list contains even one NULL, the result is 0 rows. Avoid it with NOT EXISTS or an IS NOT NULL condition.To find "employees who earn more than JONES", you first need to know JONES's salary. Instead of looking the value up and copying it by hand, you put the SELECT that finds it in parentheses and use it right where the condition goes.
SELECT ENAME, SAL FROM EMP WHERE SAL > (SELECT SAL FROM EMP WHERE ENAME = 'JONES') ORDER BY SAL DESC, ENAME;ENAME SAL
---------- ----------
KING 5000
FORD 3000
SCOTT 3000SELECT SAL FROM EMP WHERE ENAME = 'JONES' returns 2975, so the main query runs as if it were SAL > 2975.| Location | Name | Shape it must return | Example |
|---|---|---|---|
| SELECT clause | Scalar subquery | One row, one column | (SELECT ROUND(AVG(SAL)) FROM EMP) |
| FROM clause | Inline view | A table (multiple rows and columns) | FROM (SELECT DEPTNO, COUNT(*) ...) D |
| WHERE clause | Nested subquery | A shape that fits the operator | SAL > (SELECT ...) |
| HAVING clause | Nested subquery | A shape that fits the operator | HAVING AVG(SAL) > (SELECT ...) |
Put it in the SELECT clause and every row gets the same value; put it in the FROM clause and its result is used like a table. Put it in the HAVING clause and you can compare each group against an overall value. Here we find departments whose average is higher than the overall average salary (2073.21…).
SELECT DEPTNO, AVG(SAL) FROM EMP GROUP BY DEPTNO HAVING AVG(SAL) > (SELECT AVG(SAL) FROM EMP) ORDER BY DEPTNO; DEPTNO AVG(SAL)
---------- ----------
10 2916.66667
20 2175Subqueries are also classified by the number of rows they return. The number of rows decides which operators you can use.
| Type | Number of result rows | Operators used |
|---|---|---|
| Single-row subquery | 0 or 1 row | =, <>, >, >=, <, <= |
| Multi-row subquery | Multiple rows | IN, NOT IN, ANY (SOME), ALL, EXISTS |
| Multi-column subquery | Multiple columns | (col1, col2) IN (...) |
To find the employee with the highest salary in department 30, reduce the values to one with MAX.
SELECT ENAME, SAL FROM EMP WHERE SAL = (SELECT MAX(SAL) FROM EMP WHERE DEPTNO = 30);ENAME SAL
---------- ----------
BLAKE 2850Without MAX, the subquery returns 6 rows of salaries for the 6 people in department 30. = can only compare against a single value.
SELECT ENAME, SAL FROM EMP WHERE SAL = (SELECT SAL FROM EMP WHERE DEPTNO = 30);SELECT ENAME, SAL FROM EMP WHERE SAL = (SELECT SAL FROM EMP WHERE DEPTNO = 30)
*
ERROR at line 1:
ORA-01427: single-row subquery returns more than one rowIN has no problem even if the subquery returns just 1 row. When you do not know how many rows will come back, a multi-row operator is the safe choice.IN is shorthand for = OR = OR =. SAL IN (SELECT MIN(SAL) FROM EMP GROUP BY DEPTNO) picks employees whose salary equals one of the per-department minimums 800, 950, and 1300.ANY is true if at least one value in the list satisfies the condition, and ALL is true only if all of them do. The salaries in department 30 are 950, 1250, 1250, 1500, 1600, and 2850.
SELECT ENAME, SAL FROM EMP WHERE SAL > ALL (SELECT SAL FROM EMP WHERE DEPTNO = 30) ORDER BY SAL DESC, ENAME;ENAME SAL
---------- ----------
KING 5000
FORD 3000
SCOTT 3000
JONES 2975> ALL: must be greater than the largest value, 2850. BLAKE himself (2850) is left out because 2850 > 2850 is false.> ANY in the same spot, you only need to be greater than the smallest value, 950. Outside department 30, only SMITH (800) is left out.| Expression | Same meaning |
|---|---|
> ANY (list) | > MIN(list) |
< ANY (list) | < MAX(list) |
> ALL (list) | > MAX(list) |
< ALL (list) | < MIN(list) |
= ANY (list) | IN (list) |
<> ALL (list) | NOT IN (list) |
When the subquery returns 0 rows, the two operators behave in opposite ways. DEPT 40 has no employees.
SELECT COUNT(*) FROM EMP WHERE SAL > ALL (SELECT SAL FROM EMP WHERE DEPTNO = 40);
SELECT COUNT(*) FROM EMP WHERE SAL > ANY (SELECT SAL FROM EMP WHERE DEPTNO = 40); COUNT(*)
----------
14
COUNT(*)
----------
0ALL: there is no value that fails the condition, so it is true. All 14 rows come back.ANY: there is no value that satisfies the condition, so it is false. That is why > ALL and > (SELECT MAX ...) differ only when the subquery is empty. The MAX version compares against NULL and returns 0 rows.EXISTS is true if the subquery returns even 1 row. It does not look at what value is returned, so it is written like SELECT 1. Here are the departments that have an employee earning 3000 or more.
SELECT DNAME FROM DEPT D WHERE EXISTS (SELECT 1 FROM EMP E WHERE E.DEPTNO = D.DEPTNO AND E.SAL >= 3000) ORDER BY DNAME;DNAME
--------------
ACCOUNTING
RESEARCHD.DEPTNO inside the subquery is a column of the main query. This form, which re-checks the subquery for each outer row, is the correlated subquery of Day 2.When you compare two or more columns, wrap them in parentheses and compare them as a pair. This statement finds the top earner in each department.
SELECT ENAME, DEPTNO, SAL FROM EMP WHERE (DEPTNO, SAL) IN (SELECT DEPTNO, MAX(SAL) FROM EMP GROUP BY DEPTNO) ORDER BY DEPTNO, ENAME;ENAME DEPTNO SAL
---------- ---------- ----------
KING 10 5000
FORD 20 3000
SCOTT 20 3000
BLAKE 30 2850Comparing as a pair is different from comparing the columns separately. Check this using the (department, job) combinations of SMITH (20, CLERK) and ALLEN (30, SALESMAN).
SELECT ENAME, DEPTNO, JOB FROM EMP WHERE (DEPTNO, JOB) IN (SELECT DEPTNO, JOB FROM EMP WHERE ENAME IN ('SMITH', 'ALLEN')) ORDER BY DEPTNO, ENAME;ENAME DEPTNO JOB
---------- ---------- ---------
ADAMS 20 CLERK
SMITH 20 CLERK
ALLEN 30 SALESMAN
MARTIN 30 SALESMAN
TURNER 30 SALESMAN
WARD 30 SALESMANSELECT ENAME, DEPTNO, JOB FROM EMP WHERE DEPTNO IN (SELECT DEPTNO FROM EMP WHERE ENAME IN ('SMITH', 'ALLEN')) AND JOB IN (SELECT JOB FROM EMP WHERE ENAME IN ('SMITH', 'ALLEN')) ORDER BY DEPTNO, ENAME;ENAME DEPTNO JOB
---------- ---------- ---------
ADAMS 20 CLERK
SMITH 20 CLERK
ALLEN 30 SALESMAN
JAMES 30 CLERK
MARTIN 30 SALESMAN
TURNER 30 SALESMAN
WARD 30 SALESMANIf you put one column in front of the single-row operator = and the subquery returns two columns, the counts do not match.
SELECT ENAME FROM EMP WHERE DEPTNO = (SELECT DEPTNO, DNAME FROM DEPT WHERE DEPTNO = 10);SELECT ENAME FROM EMP WHERE DEPTNO = (SELECT DEPTNO, DNAME FROM DEPT WHERE DEPTNO = 10)
*
ERROR at line 1:
ORA-00913: too many valuesHere is what happens when we try to find employees who are not managers, that is, employees who do not appear as anyone's MGR, using NOT IN.
SELECT COUNT(*) FROM EMP WHERE EMPNO NOT IN (SELECT MGR FROM EMP); COUNT(*)
----------
0There are 8 employees with no subordinates, yet we got 0. The reason is that KING's NULL is mixed into the MGR column.
EMPNO NOT IN (7902, 7698, …, NULL) is EMPNO <> 7902 AND … AND EMPNO <> NULL.EMPNO <> NULL is UNKNOWN, neither true nor false. If even one UNKNOWN is in a chain of AND conditions, the whole thing can never be true.IN expands to OR, so even with a NULL mixed in, it is true if the value equals any of the others. The problem only arises with NOT IN.⚠️ Pitfall: If the result of a
NOT INsubquery contains even one NULL, the result is 0 rows with no error. AddWHERE MGR IS NOT NULLto the subquery or switch toNOT EXISTS.
SELECT COUNT(*) FROM EMP WHERE EMPNO NOT IN (SELECT MGR FROM EMP WHERE MGR IS NOT NULL); COUNT(*)
----------
8SELECT COUNT(*) FROM EMP E WHERE NOT EXISTS (SELECT 1 FROM EMP M WHERE M.MGR = E.EMPNO); COUNT(*)
----------
8NOT EXISTS only checks "is there no matching row". There is no place for a NULL comparison to interfere, so the result is the correct 8.A WHERE-clause subquery only passes a list of values, so order has no meaning. Oracle raises a syntax error if you use ORDER BY here.
SELECT COUNT(*) FROM EMP WHERE DEPTNO IN (SELECT DEPTNO FROM DEPT ORDER BY DEPTNO);SELECT COUNT(*) FROM EMP WHERE DEPTNO IN (SELECT DEPTNO FROM DEPT ORDER BY DEPTNO)
*
ERROR at line 1:
ORA-00907: missing right parenthesisROWNUM, rely on this property (W6).| Location | ORDER BY |
|---|---|
| WHERE / HAVING subquery | Not allowed (ORA-00907) |
| FROM-clause inline view | Allowed |
| Main query | Once, at the very end |
FETCH FIRST n ROWS ONLY after ORDER BY, it is also accepted in a WHERE-clause subquery, because the sort is used to pick the "first n rows". The explanations and questions in this note assume the case where ORDER BY is used without FETCH.Tomorrow we cover correlated subqueries that reference outer rows, scalar subqueries in the SELECT clause, and inline views and views in the FROM clause.
= and >.IN, ANY, ALL, and EXISTS.(A, B) IN (...).Click a choice to reveal the answer and explanation.
Question 1
Department 30 of EMP has 6 employees. Which is the correct result of running the following SQL in Oracle? ```sql SELECT ENAME FROM EMP WHERE SAL = (SELECT SAL FROM EMP WHERE DEPTNO = 30); ```
Question 2
The MGR column of EMP contains one NULL (KING's), and 8 employees have no subordinates. What is the result of the following SQL? ```sql SELECT COUNT(*) FROM EMP WHERE EMPNO NOT IN (SELECT MGR FROM EMP); ```
Question 3
Department 30 has employees. Which condition produces the same result as SAL > ALL (SELECT SAL FROM EMP WHERE DEPTNO = 30)?
Question 4
Which of the following raises an error when run in Oracle?
Question 5
SMITH is (20, CLERK) and ALLEN is (30, SALESMAN). In EMP, (20, CLERK) has 2 people, (30, SALESMAN) has 4, and (30, CLERK) has 1. What is the result of the following SQL? ```sql SELECT COUNT(*) FROM EMP WHERE (DEPTNO, JOB) IN (SELECT DEPTNO, JOB FROM EMP WHERE ENAME IN ('SMITH', 'ALLEN')); ```