Sub Queries
Sub Queries
Chapter 6
Objectives
After completing this lesson, you should be able to do the follovving:
Describe the types of problems that
Subqueries
SELECT select_list FROM table
A subquery is a SELECT statement that is embedded in a clause of another SELECT statement. You can build powerful statements out of simple ones by using subqueries. They can be very useful when you need to select rows from a table with a condition that depends on the data in the table itself. You can place the subquery in a number of SQL clauses WHERE clauss HAVING clause FROM clause ln the syntax; operator includes a comparison operatr such as >, =, or IN
Note: Comparison operutors fall ito two classes: single-row operators ( > , = , >= , < , < > , <= ) and multiple-row operators ( IN , ANY , ALL ).
Using a Subquery
SELECT ename FROM EMP WHERE sal > ( SELECT sal FROM emp WHERE empno=7566);
ENAME FORD SCOTT KING FORD
Using a Subquery
SELECT ename, sal, deptno, job FROM EMP WHERE job = ( SELECT job FROM emp WHERE empno=7369);
ENAME ADAMS JAMES MILLER SMITH ADAMS JAMES MILLER 7 rows selected. SAL 1100 950 1300 800 1100 950 1300 DEPTNO 20 30 10 20 20 30 10 JOB CLERK CLERK CLERK CLERK CLERK CLERK CLERK
SELECT ename, sal, deptno FROM EMP WHERE sal IN ( SELECT MIN(sal) FROM emp GROUP BY deptno );
ENAME JAMES SMITH MILLER SAL 950 800 1300 DEPTNO 30 20 10
SELECT empno, ename, job FROM emp WHERE sal < ANY ( SELECT sal FROM emp WHERE job = 'CLERK' );
EMPNO 7369 7900 7876 7521 7654 ENAME SMITH JAMES ADAMS WARD MARTIN JOB CLERK CLERK CLERK SALESMAN SALESMAN
SELECT empno, ename, job FROM emp WHERE sal < ANY ( SELECT sal FROM emp WHERE job = 'CLERK' ) AND job <> 'CLERK' ;
EMPNO ENAME 7521 WARD 7654 MARTIN JOB SALESMAN SALESMAN
SELECT empno, ename, job FROM emp WHERE sal > ALL ( SELECT sal FROM emp WHERE job = 'CLERK' ) AND job <> 'CLERK' ;
EMPNO 7499 7566 7698 7782 7788 7839 7844 7902 8 rows selected. ENAME ALLEN JONES BLAKE CLARK SCOTT KING TURNER FORD JOB SALESMAN MANAGER MANAGER MANAGER ANALYST PRESIDENT SALESMAN ANALYST
SELECT empno, ename, job FROM emp WHERE sal > ALL ( SELECT AVG(sal) FROM emp GROUP BY deptno ) ;
EMPNO 7566 7788 7839 7902 ENAME JONES SCOTT KING FORD JOB MANAGER ANALYST PRESIDENT ANALYST
10
row subqueries.
multiple-row subqueries.
11
Types of Subqueries
Single-row subquery Multiple-row subquery Multiple-column subquery
Types of Subqueries Single-row subgueries: Muliple-row subqueries: Muliple-column subqueries: Queries that return only one row from the inner SELECT stntenent QUERIES that return more than one rows from the inner SELECT statement QUERIES that return more than one column from the inner SELECT statement.
12
Single-Row Subqueries
Return only one row
Operator
Meaning Equal to
Greater than
Greater than or equal to Less than Less than or equal to Not equal to
SELECT ename, job FROM EMP WHERE job = ( SELECT job FROM emp WHERE empno=7369 ) ; ENAME ADAMS JAMES MILLER SMITH ADAMS JAMES MILLER 7 rows selected. CLERK CLERK CLERK CLERK CLERK CLERK CLERK JOB
13
14
15
first. The Oracle Server returns results into the HAVING clause of the main query.
SELECT job, AVG(sal) FROM emp GROUP BY job HAVING AVG(sal) = ( SELECT MIN(AVG(sal)) FROM emp GROUP BY job ); JOB CLERK AVG(SAL) 1037,5
16
(SELECT *
MIN(sal)
ERROR at line 4: ORA-01427: single-row subquery returns more than one row
17
18
Multiple-Row Subqueries
Meaning Equal to any member in the list Compare value to each value returned by the subquery Compare value to every value returned by the subquery
ename, sal, deptno emp sal IN (SELECT MIN(sal) FROM emp GROUP BY deptno);
SAL 800 950 1300 DEPTNO 20 30 10
19
20
21
Summary
Subqueries are useful when a query is based on unknown values.
22
select_list
select_list
23
Practice Overview
Creating subqueries to query values based on unknown criteria Using subqueries to find out what values exist in one set of data and not in another
1. Write a query to display the employee name and hiredate for all employees in the same department as Blake. Exclude Blake.
SELECT ename, hiredate FROM emp WHERE deptno = ( SELECT deptno FROM emp WHERE ename = 'BLAKE') AND ename <> 'BLAKE'; 2. Create a query to display the employee number and name for all employees who earn more than the average salary. Sort the results in descending order of salary. SELECT empno, ename FROM emp WHERE sal > ( SELECT AVG(sal) FROM emp );
3. Write a query to display the employee number and name for all employees who work in a department with any employee whose name contains a T. Save your SQL statemant in a file called p6q3.sql .
SELECT empno, ename FROM emp WHERE deptno IN ( SELECT deptno FROM emp WHERE ename LIKE '%T%' );
24
4. Display the employee name, department number, and job title for all
5. Display the employee name and salary of all employees who report to
King. Solution with self join: SELECT e.ename, e.sal FROM emp e , emp d WHERE e.mgr = d.empno AND d.ename ='KING'; Solution with subquery:
25
SELECT ename, sal FROM emp WHERE mgr = (SELECT empno FROM emp WHERE ename = 'KING' ); 6. Display the department number, name,, and job for all employees in the Sales department. SELECT e.deptno, e.ename, e.job , d.dname FROM emp e , dept d WHERE e.deptno = d.deptno AND d.dname = 'SALES' If yo u have time, complete the following exercises:
7. Modify p6q3.sql to display the employee number, name, and salary for all
employees who earn more than the average salary and who work in a department with any employee with a T in their name. Rerun your query.
SELECT empno, ename , sal FROM emp WHERE sal > (SELECT AVG (sal) FROM emp ) AND deptno IN ( SELECT deptno FROM emp WHERE ename LIKE '%T%');
EMPNO 7902 FORD 7788 SCOTT 7566 JONES 7698 BLAKE ENAME SAL 3000 3000 2975 2850
26
Exists In the previous section, we used IN to link the inner query and the outer query in a subquery statement. IN is not the only way to do so -- one can use many operators such as >, <, or =. EXISTS is a special operator that we will discuss in this section. EXISTS simply tests whether the inner query returns any row. If it does, then the outer query proceeds. If not, the outer query does not execute, and the entire SQL statement returns nothing. The syntax for EXISTS is: SELECT "column_name1" FROM "table_name1" WHERE EXISTS ( SELECT * FROM "table_name2" WHERE [Condition] )
27
SELECT ename, deptno FROM emp WHERE EXISTS (SELECT * FROM emp WHERE sal >3500 )
ENAME SMITH ALLEN WARD JONES MARTIN BLAKE CLARK SCOTT KING TURNER ADAMS JAMES FORD MILLER 14 rows returned in 0,01 seconds DEPTNO 20 30 30 20 30 30 10 20 10 30 20 30 20 10
SELECT ename, deptno FROM emp WHERE EXISTS (SELECT * FROM emp WHERE sal >3500 )
no data found
28
WHERE clause
Most often, the subquery will be found in the WHERE clause. These subqueries are also called nested subqueries.
select * from all_tables tabs where tabs.table_name in (select cols.table_name from all_tab_columns cols where cols.column_name = 'ENAME');
Limitations: Oracle allows up to 255 levels of subqueries in the WHERE clause.
29
FROM clause
A subquery can also be found in the FROM clause. These are called inline views. For example:
select dept.deptno, subquery1.total_amt from dept, ( select emp.deptno, Sum(emp.sal) total_amt from emp group by deptno) subquery1 WHERE subquery1.deptno = dept.deptno
DEPTNO 30 10 20
3 rows returned in 0,02 seconds
30
SELECT clause
A subquery can also be found in the SELECT clause. For example:
select tbls.owner, tbls.table_name, (select count(column_name) as total_columns from all_tab_columns cols where cols.owner = tbls.owner and cols.table_name = tbls.table_name) subquery2 from all_tables tbls;
OWNER SYS SYS SYS SYS SYS SYSTEM SYSTEM CTXSYS CTXSYS CTXSYS MDSYS MDSYS MDSYS MDSYS MDSYS
TABLE_NAME DUAL SYSTEM_PRIVILEGE_MAP TABLE_PRIVILEGE_MAP STMT_AUDIT_OPTION_MAP AUDIT_ACTIONS DEF$_TEMP$LOB HELP DR$OBJECT_ATTRIBUTE DR$POLICY_TAB DR$NUMBER_SEQUENCE OGIS_SPATIAL_REFERENCE_SYSTEMS OGIS_GEOMETRY_COLUMNS SDO_UNITS_OF_MEASURE SDO_PRIME_MERIDIANS SDO_ELLIPSOIDS
SUBQUERY2 1 3 2 3 2 3 3 14 2 1 5 10 12 6 10
More than 15 rows available. Increase rows selector to view more rows. 15 rows returned in 0,15 seconds CSV Export
(select count(column_name) as total_columns from all_tab_columns cols where cols.owner = tbls.owner and cols.table_name = tbls.table_name) subquery2
31
The subquery has been aliased with the name subquery2. This will be the name used to reference this subquery or any of its fields. The trick to placing a subquery in the select clause is that the subquery must return a single value. This is why an aggregate function such as SUM, COUNT, MIN, or MAX is commonly used in the subquery.
32
How to work with a dual table. How to analyze and identify the steps needed to deal with a sub-query. How to frame queries for each of the identified steps. How to combine all the framed queries and design a single command to retrieve the final output.
A primer on the dual table in Oracle This section mainly explains the dual table in Oracle. I shall use this table in a creative manner wherever required in this article as well as upcoming articles. If you are already familiar with the "dual table, feel free to skip to the next section. What is a dual table? It is a simple table which is created/installed automatically during the installation of the Oracle database. To understand it, let us consider the following SELECT statement: SELECT 123 FROM dual; The above statement simply returns 123. Let us work with another statement: SQL> SELECT 10,20,Veri Taban Ynetim Sistemleri,3400 FROM dual; Results Explain Describe Saved SQL History
10 10
20 20
3400 3400
CSV Export
This returns any constant values you provide as columns. So dual is just a convenience table. It is simply a one column and one row table that exists as part of SYS user. You can use the DESC command to display the structure of a dual table as follows:
33
DESC dual;
The above statement returns the following output:
ResultsExplain
Describe
Saved SQL
History
Table DUAL
Column DUMMY
Length 1
Precision -
Scale -
Primary Key -
Nullable
Default -
Comment -
You can observe that there exists only one column named dummy in the "dual" table. Similarly, you can even display all the rows in the "dual" table as follows:
34
Stepping through Sub-Queries in Oracle - The simplest sub-query in Oracle (Page 2 of 5 ) Before explaining sub-queries, let us consider the following scenario: SQL> SELECT empno,ename,sal,deptno FROM emp WHERE sal = 5000; EMPNO ENAME SAL DEPTNO
1 rows selected Let us modify the above statement as follows: SQL> SELECT empno,ename,sal,deptno FROM emp WHERE sal = (SELECT 5000 FROM dual); I already explained the dual table in the previous section. In the above statement, I have two SELECT statements as part of a single command. The following is the order in which the above statement gets executed:
The innermost query gets executed first. In this case, the query select 5000 from dual gets executed first. Once the innermost query gets executed, it returns a value to the immediate outer query. In this case, it is 5000. The entire innermost query gets replaced with the new value returned by it. In this case, the outer query virtually becomes select empno, ename, sal, deptno from emp where sal = 5000. And finally, the outer query gets executed, which retrieves KINGs details.
In the above case, I used a SELECT query as part of another SELECT query; thus it is called a sub-query. You can even modify the above statement to include an expression as follows: SQL> SELECT empno,ename,sal,deptno FROM emp WHERE sal = (SELECT 2000+3000 FROM dual); The above statement first evaluates 2000+3000 (which results in 5000) as part of executing the inner query. Based on the returned value (which is 5000), the outer query gets executed. The next section will show you a few simple and practically used sub-queries.
35
Stepping through Sub-Queries in Oracle - A sub-query with aggregate functions (or group functions) in Oracle (Page 3 of 5 ) I already introduced sub-queries in the previous section. In this section, I shall start giving you some practical examples. Let us consider that I would like to retrieve the details of the highest paid employee. Let us write the question in a meaningful manner and identify the steps as follows:
From the above figure, you have two steps to work with for the query. The following is the order you must follow (based on the above figure):
Find the highest salary from the table (1) Based on the value you get, retrieve the employee details like empno, ename, etc. belonging to that salary. (2)
The following is the statement which retrieves the highest salary: SELECT MAX(sal) FROM emp To retrieve an employee's details based on a given salary (say 5000), the query would be as follows: SELECT empno,ename,sal,deptno FROM emp WHERE sal = 5000 Just replace the value 5000 with the query that gives you the highest salary. The complete statement would be as follows: SQL> SELECT empno,ename,sal,deptno FROM emp WHERE sal = (SELECT MAX(sal) FROM emp); Now, let us walk through its execution:
The innermost query gets executed first. In this case, the query select max(sal) from emp gets executed first. It retrieves the highest value in the column sal from the table emp. Once the innermost query gets executed, it returns a value to the immediate outer query. In this case, it is 5000. The entire innermost query gets replaced with the new value returned by it. In this case, the outer query virtually becomes select empno, ename, sal, deptno from emp where sal = 5000. 36
And finally, the outer query gets executed, which retrieves KINGs details.
Let us end this section with a final touch. Why cant I write the above query as follows? SQL> SELECT empno,ename,sal,deptno FROM emp WHERE sal = MAX(sal) Or even the following: SQL> SELECT empno,ename,sal,deptno FROM emp WHERE MAX(sal) = sal None of the above two queries get executed successfully. The reason is that a condition in a WHERE clause cannot contain any aggregate function (or group function) without a subquery!
37
Stepping through Sub-Queries in Oracle - Designing sub-queries to deal with more than one table (or different tables) (Page 4 of 5 ) Let us consider that I would like to retrieve KING's department name. All department names are available in the table dept, which is quite different from emp table. Let us write the question in a meaningful manner and identify the steps as follows:
From the above figure, you have two steps to go through with the query. The following is the order you must follow (based on the above figure):
Find KING's department number (1). Based on the value you get, retrieve the department details like dname, loc. etc belonging to that department number (2)
The following is the statement which retrieves KING's department number: SELECT deptno FROM emp WHERE ename = KING To retrieve department details based on a given department number (say 30), the query would be as follows: SELECT dname FROM dept WHERE deptno = 30 Just replace the value 30 with the query that gives you KING's department number. The complete statement would be as follows: SQL> SELECT dname FROM dept WHERE deptno = (SELECT deptno FROM emp WHERE ename=KING); Now, let us walk through its execution:
The innermost query gets executed first. In this case, the query select deptno from emp where ename=king gets executed first. It retrieves KING's department number. Once the innermost query gets executed, it returns a value to the immediate outer query. In this case, it is 10. The entire innermost query gets replaced with the new value returned by it. In this case, the outer-query virtually becomes select dname from dept where deptno = 10. And finally, the outer query gets executed, which retrieves KINGs department details
38
Stepping through Sub-Queries in Oracle - An example of a nested sub-query (or multilevel sub-query) (Page 5 of 5 ) Let us consider that I would like to retrieve the department name of the highest paid employee. Let us write the question in a meaningful manner and identify the steps as follows:
From the above figure, you have three steps to go through with the query. The following is the order you must follow (based on the above figure):
Find the highest salary (1). Based on the value you get, retrieve the department number belonging to that salary (2). Based on the value you get, find the department name belonging to that department number (3).
The following is the statement which finds the highest salary: SELECT MAX(sal) FROM emp To retrieve a department number based on a given salary (say 2000), the query would be as follows: SELECT deptno FROM emp WHERE sal = 2000 To retrieve a department name based on a given department number (say 20), the query would be as follows: SELECT dname FROM dept WHERE deptno = 20 Combining all of the above queries according to the order given above, the new query would be as follows: SQL> SELECT dname FROM dept WHERE deptno = (SELECT deptno FROM emp WHERE sal = (SELECT MAX(sal) FROM EMP)); You can observe the following figure to understand how the execution takes place. You can also observe the underlined columns on how they relate logically: 39
The innermost query gets executed first. In this case, the query select max(sal) from emp gets executed first. It retrieves the highest salary. In this case it would be 5000. The entire innermost query gets replaced with the new value returned by it. In this case, the immediate outer query virtually becomes select deptno from emp where sal = 5000. Once this query gets executed, it returns a department number, which is 10 in this case. And finally, the outermost query virtually becomes select dname from dept where deptno = 10, which retrieves KINGs department details.
Any bugs, doubts, suggestions, feedback etc. are highly appreciated at http://jagchat.spaces.live.com
40
The above would never work and results in giving you the invalid relational operator error. This is because you can compare only one value at a time and not more than one. The following is a small trick to overcome the problem:
I just made both of those two values part of the sub-query and it works fine! Both of those values are not from any table, so I used dual. Now you can observe the power of the dual table. If you want to learn more about dual, please refer to my first article in this series. Let us work with a more practical sub-query rather than with a dual table as follows: SELECT * FROM emp WHERE (sal,mgr) = (SELECT sal,mgr FROM emp WHERE sal = (SELECT MIN(sal) FROM EMP WHERE sal > (SELECT MIN(sal) FROM emp)))
41
EMPNO 7900
ENAME JAMES
JOB CLERK
MGR 7698
COMM -
DEPTNO 30
The above returns all the employees who are earning the same salary and working under the same manager of the employee earning the second least salary!
42
Inserting Sub-Queries in SELECT Statements in Oracle - SubQueries returning single and multiple values in Oracle
Let us consider the following statement:
SELECT * FROM emp WHERE empno IN (SELECT empno FROM emp WHERE sal = (SELECT MAX(sal) FROM EMP
43
The above query gives us all the employees earning the second highest salary! In the above case, the second level sub-query returns more than one value (or two employee numbers). Therefore, I provided the IN operator as part of the condition. If you replace the IN with = in the above query, it will return with an error stating single row sub-query returns more than one row. When you receive such an error, try to replace = with IN wherever appropriate.
44
Inserting Sub-Queries in SELECT Statements in Oracle - Sub-Queries as part of the BETWEEN operator in Oracle
BETWEEN is a unique operator in Oracle. Let me show you a small example of this:
SELECT * FROM emp WHERE sal BETWEEN (SELECT 1000 FROM dual) AND 3000
I just replaced 1000 with a simple sub-query using dual. If you are new to the dual table, please refer to my first article in this series. Let us work a bit more practically as follows:
SELECT * FROM emp WHERE sal BETWEEN (SELECT MIN(sal) FROM emp) and 2000
We can even work a bit differently. Let us go through the following query:
SELECT Grade FROM salgrade WHERE (SELECT MAX(sal) FROM emp) BETWEEN losal AND hisal
The above gives you the salary grade of the highest salary!
45
Inserting Sub-Queries in SELECT Statements in Oracle - Derived tables (or inline views) with Sub-Queries in Oracle
Can we write sub-queries in (or as part of) the FROM clause? The answer is YES and you can use it very efficiently in some scenarios. Let us consider the following statement:
SELECT empno,ename,sal,AnnSal FROM ( SELECT empno,ename,sal,sal*12 AS AnnSal FROM emp ) WHERE AnnSal > 30000
The above statement is totally different from the previous one. Within the above statement, the outer query doesnt rely on any specific physical table. The output (or result set) of the inner query is considered as a table for the outer query! The inner query is very similar to that of a view which doesnt have any physical view name, and it gets created and destroyed on the fly. So, according to the inner query, it retrieves four columns (empno, ename, sal, AnnSal). The outer query can work with all four columns as if they are directly from a physical table. As you are trying to define/derive your own table of information from an existing physical table, you call it as a derived table (or inline view). Even the derived tables can be nested to any number of levels with further sub-derived tables as part of FROM clauses.
46
Inserting Sub-Queries in SELECT Statements in Oracle - SubQueries with CASE structure in Oracle SELECT statements
Now let us go through an interesting topic on CASE structure. Let us see how a CASE structure works. Consider the following statement:
SELECT empno, ename, CASE job WHEN 'SALESMAN' THEN 'SALES' WHEN 'MANAGER' THEN 'MGMT' ELSE job END AS jobfunction, sal FROM emp
EMPNO 7369 7499 7521 7566 7654 7698 7782 7788 7839 7844 7876 7900 7902 7934 ENAME SMITH ALLEN WARD JONES MARTIN BLAKE CLARK SCOTT KING TURNER ADAMS JAMES FORD MILLER JOBFUNCTION CLERK SALES SALES MGMT SALES MGMT MGMT ANALYST PRESIDENT SALES CLERK CLERK ANALYST CLERK SAL 800 1600 1250 2975 1250 2850 2450 3000 5000 1500 1100 950 3000 1300
CSV Export
When the above query is executed, it returns four columns (empno, ename, jobfunction, sal). The only eye-catching issue from the above is the following structure:
CASE job WHEN 'SALESMAN' THEN 'SALES' WHEN 'MANAGER' THEN 'MGMT' ELSE job END AS jobfunction
47
If the value of job is salesman return sales. If the above condition fails and if the value of job is manager return mgmt. If both of the above conditions fail then return the same value of job. All the values must be returned in a new column with the heading jobfunction.
You need to observe that I specified the column (job) along with CASE. The conditions of WHEN work with the values available within that column. We can even work with different relational (and SQL) operators within the WHEN condition as shown in the following example:
SELECT empno, ename, CASE WHEN comm IS NULL OR comm=0 THEN '-NA-' ELSE TO_CHAR(comm) END AS comm, sal FROM emp
EMPNO 7369 7499 7521 7566 7654 7698 7782 7788 7839 7844 7876 7900 7902 7934 ENAME SMITH ALLEN WARD JONES MARTIN BLAKE CLARK SCOTT KING TURNER ADAMS JAMES FORD MILLER COMM -NA300 500 -NA1400 -NA-NA-NA-NA-NA-NA-NA-NA-NACSV Expor SAL 800 1600 1250 2975 1250 2850 2450 3000 5000 1500 1100 950 3000 1300
In the above case, the conditions are directly used within the WHEN statement and you need not specify any column with the CASE. Finally, you can even work with sub-queries within the CASE structure as follows:
48
SELECT empno, ename, CASE WHEN sal >= (SELECT avg(sal) FROM emp) THEN 'HIGH' ELSE 'LOW' END AS pay, sal FROM emp
The above returns a new column named pay, which contains either HIGH or LOW based on their salary compared to the average salary of all employees.
49
Inserting Sub-Queries in SELECT Statements in Oracle - SubQueries as (or part of) columns in Oracle SELECT statements
Before getting into sub-queries as part of columns, let us look at the following small query:
AS AS AS AS
Everyone can easily understand that the above query returns only one row containing four values of aggregation. Let us rewrite the above query using sub-queries to get the same output.
AS AS AS AS
You can observe that I replaced all aggregate functions with sub-queries! Another important issue to concentrate on in the above query is the dual table. As the sub-queries in the above statement are working individually by themselves, I need not work with any table and thus I used the dual table. If you want to learn more about the dual table, please go through my first article in this same series. Now, let us look at an interesting query which deals with sub-queries at both the column level and the CASE level. The following is the query:
50
SELECT empno, ename, sal AS salary, ROUND((sal -(SELECT AVG(sal) FROM emp)),2) AS avgcompare, CASE WHEN sal >= (SELECT AVG(sal) FROM emp) THEN 'HIGH' ELSE 'LOW' END AS paying FROM emp
The following is the sample output of the above query: EMPNO ENAME SALARY AVGCOMPARE PAYING ------- ---------- -------- ------------ -----7839 KING 5000 2926.79 HIGH 7698 BLAKE 2850 776.79 HIGH . . 7654 MARTIN 1250 -823.21 LOW 7499 ALLEN 1600 -473.21 LOW Any bugs, doubts, suggestions, feedback etc. are highly appreciated at http://jagchat.spaces.live.com
51