Wednesday, January 29, 2014

Write Smarter Queries with the SQL WITH Clause

sql-with-clause
The WITH clause, also known as the subquery factoring clause, was introduced in Oracle 9i as a way to make queries more efficient and readable. Let’s see how WITH works with a basic example.
Say we have three departments at our workplace – Accounting, Research and Sales – and we want to select only those departments with a total salary more than the average total.
The total salary per department is:
1
2
3
4
5
6
7
8
9
10
11
SQL> select dname, sum(sal) as dept_total
  from emp, dept
  where emp.deptno = dept.deptno
  group by dname
  5  /
 
DNAME          DEPT_TOTAL
-------------- ----------
ACCOUNTING           9150
RESEARCH            10875
SALES                9400
The average total salary per department is:
1
2
3
4
5
6
7
8
SQL>       select sum(sal) * 1/3
  2        from emp, dept
  3        where emp.deptno = dept.deptno
  4  /
 
SUM(SAL)*1/3
------------
  9808.33333
So the final query, without the WITH clause, will be:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SQL> select dname, sum(sal) as dept_total
  from emp, dept
  where emp.deptno = dept.deptno
  group by dname having
  5      sum(sal) >
  6      (
  7        select sum(sal) * 1/3
  8        from emp, dept
  9        where emp.deptno = dept.deptno
 10     )
 11  order by sum(sal) desc;
 
DNAME          DEPT_TOTAL
-------------- ----------
RESEARCH            10875
Notice the problem? We’re duplicating a large part of the query, calculating the sum twice. This makes the query inefficient and poor to read.
That’s when the WITH clause comes to our rescue. When a subquery is being processed multiple times, WITH lets you factor it out, give it a name and then reference the name wherever needed in the query.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
SQL> with sumsal as
  2  (
  3   select dname, sum(sal) as dept_total
  4   from emp, dept
  5   where emp.deptno = dept.deptno
  6   group by dname
  7  )
  -- End of factored subquery
  -- Main query starts
 10  select dname, dept_total
 11  from sumsal        -- Reference #1
 12  where dept_total >
 13      (
 14         select sum(dept_total) * 1/3
 15         from sumsal -- Reference #2
 16       )
 17  order by dept_total desc;
 
DNAME          DEPT_TOTAL
-------------- ----------
RESEARCH            10875
The rewritten query using WITH not just improves performance but also makes code easier to understand and maintain.

The WITH Clause Syntax

1
2
3
4
5
with clause-name AS
(
subquery
)
main query referencing clause-name;
The syntax of the WITH clause is analogous to defining a function and invoking it multiple times in the main program. There is one difference though, which we’ll look at in the next article: Why the SQL WITH clause is not exactly like a function definition.

No comments:

Post a Comment