Write Smarter Queries with the SQL WITH Clause

July 26, 2010

in Keywords, Performance, SQL

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:

SQL> select dname, sum(sal) as dept_total
  2  from emp, dept
  3  where emp.deptno = dept.deptno
  4  group by dname
  5  /

DNAME          DEPT_TOTAL
-------------- ----------
ACCOUNTING           9150
RESEARCH            10875
SALES                9400

The average total salary per department is:

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:

SQL> select dname, sum(sal) as dept_total
  2  from emp, dept
  3  where emp.deptno = dept.deptno
  4  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.

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  )
  8  -- End of factored subquery
  9  -- 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

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 in the older versions of Oracle, which we’ll look at in the next article.

[Photo by hidden side]

{ 1 comment… read it below or add one }

1 Lebowski March 13, 2017 at 7:21 pm

Nice explanation but maybe for bonus points someone could update the solution to also remove the “1/3” magic number that seems to be apriori knowledge of the number of departments?

Leave a Comment

Previous post:

Next post: