Dear readers of our blog, we'd like to recommend you to visit the main page of our website, where you can learn about our product SQLS*Plus and its advantages.
 
SQLS*Plus - best SQL Server command line reporting and automation tool! SQLS*Plus is several orders of magnitude better than SQL Server sqlcmd and osql command line tools.
 

REQUEST COMPLIMENTARY SQLS*PLUS LICENCE

Enteros UpBeat offers a patented database performance management SaaS platform. It proactively identifies root causes of complex revenue-impacting database performance issues across a growing number of RDBMS, NoSQL, and deep/machine learning database platforms. We support Oracle, SQL Server, IBM DB2, MongoDB, Casandra, MySQL, Amazon Aurora, and other database systems.

Oracle WITH operator

17 August 2020

Oracle WITH operator

The Oracle PL/SQL operator WITH allows you to give a subquery block a name/allowable that can be referenced in several places in the main SQL query.

The name assigned to the subquery is processed as if it were a built-in view or table. The WITH SQL statement is essentially a replacement for a regular subquery.

Oracle PL/SQL WITH syntax with one subquery

WITH query_name AS (SELECT FROM table_A)
SELECT column_list
FROM query_name [,table_name]
[WHERE conditions]

or

Oracle PL/SQL WITH syntax with multiple subqueries

WITH query_name_A AS
(SELECT FROM table_A),
query_name_B AS
([SELECT FROM query_name_A] | [SELECT FROM table_B]).
SELECT column_list
FROM query_name_A, query_name_B [,table_X | Join table_Z].
[WHERE conditions]

  • expressions – fields or calculations under request.
  • column_list – fields or calculations of the main request.
  • table_A, table_B, table_X, table_Z – tables or connections for subqueries.
  • query_name_A, query_name_B – the alias of the subquery. If there are several subqueries, they are listed by a comma.
  • WHERE conditions – conditions that must be met for main queries.

Note: 

  • Formally, the WITH offer is called subquery factoring.
  • The SQL WITH offer is used when a subquery is executed several times.
  • WITH subqueries are listed by a comma.

Application

  • SQL-proposal WITH was introduced by Oracle in Oracle 9i database release 2.
  • Starting with Oracle Database 12c Release 1 (12.1), functions and procedures can be defined in the WITH operator.

Examples of Oracle PL/SQL WITH Operator

For an example of using a WITH operator, with one and two subqueries, let’s create several tables with data.

--Remove the tables from the database, if any.
--DROP TABLE EMP PURGE;
--DROP TABLE DEPT PURGE;

--Create two EMP and DEPT tables:
CREATE TABLE DEPT (
DEPTNO NUMBER(2) CONSTRAINT PK_DEPT PRIMARY KEY,
DNAME VARCHAR2(14),
CITY VARCHAR2(15)
) ;

CREATE TABLE EMP (
EMPNO NUMBER(4) CONSTRAINT PK_EMP PRIMARY KEY,
ENAME VARCHAR2(10),
JOB VARCHAR2(9),
MGR NUMBER(4),
HIREDATE DATE,
SAL NUMBER(7,2),
COMM NUMBER(7,2),
DEPTNO NUMBER(2) CONSTRAINT FK_DEPTNO REFERENCES DEPT
);

-- And add the following data to the EMP and DEPT tables:
--DEPT .
INSERT INTO DEPT VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO DEPT VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO DEPT VALUES (30, 'SALES', 'CHICAGO');
INSERT INTO DEPT VALUES (40,'OPERATIONS','BOSTON');
--EMP
INSERT INTO EMP VALUES (7369, 'SMITH', 'CLERK',7902,to_date('17.12.1980', 'dd.mm.yyyy'),800,NULL,20);
INSERT INTO EMP VALUES (7499, 'ALLEN', 'SALESMAN',7698,to_date('20.2.1981', 'dd.mm.yyyy'),1600,300,30);
INSERT INTO EMP VALUES (7521, 'WARD', 'SALESMAN',7698,to_date('22.2.1981', 'dd.mm.yyyy'),1250,500,30);
INSERT INTO EMP VALUES (7566, 'JONES', 'MANAGER',7839,to_date('2.4.1981', 'dd.mm.yyyy'),2975,NULL,20);
INSERT INTO EMP VALUES (7654, 'MARTIN', 'SALESMAN',7698,to_date('28.9.1981', 'dd.mm.yyyy'),1250,1400,30);
INSERT INTO EMP VALUES (7698, 'BLAKE', 'MANAGER',7839,to_date('1.5.1981', 'dd.mm.yyyy'),2850,NULL,30);
INSERT INTO EMP VALUES (7782, 'CLARK', 'MANAGER',7839,to_date('9.6.1981','dd.mm.yyyy'),2450,NULL,10);
INSERT INTO EMP VALUES (7788, 'SCOTT', 'ANALYST',7566,to_date('13.7.87', 'dd.mm.rr').85,3000,NULL,20);
INSERT INTO EMP VALUES (7839, 'KING', 'PRESIDENT',NULL,to_date('17.11.1981', 'dd.mm.yyyy'),5000,NULL,10);
INSERT INTO EMP VALUES (7844, 'TURNER', 'SALESMAN',7698,to_date('8.9.1981', 'dd.mm.yyyy'),1500,0,30);
INSERT INTO EMP VALUES (7876, 'ADAMS', 'CLERK',7788,to_date('13.7.87', 'dd.mm.rr').51,1100,NULL,20);
INSERT INTO EMP VALUES (7900, 'JAMES', 'CLERK',7698,to_date('3.12.1981', 'dd.mm.yyyy'),950,NULL,30);
INSERT INTO EMP VALUES (7902, 'FORD', 'ANALYST',7566,to_date('3.12.1981', 'dd.mm.yyyy'),3000,NULL,20);
INSERT INTO EMP VALUES (7934, 'MILLER', 'CLERK',7782,to_date('23.1.1982', 'dd.mm.yyyy'),1300,NULL,10);
COMMIT;

WITH example with one subquery

Consider the example of the sql operator with a single subquery to understand how to use the with operator in Oracle PL/SQL. For example:

WITH employee AS (SELECT * FROM emp)
SELECT * FROM employee WHERE empno > 7900
UNION ALL
SELECT * FROM employee WHERE sal > 3000

In this example, we created a subquery with the nickname employee from the Employees table. Then, using two main queries and the UNION ALL operator, we requested data from all employees with empno > 7900 and all employees with sal > 3000.

In the following example, we want to know how many people are in the department for each employee.

WITH dept_count AS (
SELECT deptno, COUNT(*) AS dept_count
FROM emp
GROUP BY deptno)
SELECT e.ename AS employee_name,
dc.dept_count AS emp_dept_count
FROM emp e
JOIN dept_count dc ON e.deptno = dc.deptno;

Example of WITH with two subqueries

For an example of using a WITH operator, let’s create some tables with data.

For example, we may need to select departments with salaries above average. To do this, first define the amount of salaries by department in the first subquery dept_costs.

Then in the second subquery avg_cost let’s define the average salary by department. For example:

WITH
dept_costs AS (
SELECT dname, SUM(sal) dept_total
FROM emp e, dept d
WHERE e.deptno = d.deptno
GROUP BY dname
),
avg_cost AS (
SELECT SUM(dept_total)/COUNT(*) avg
FROM dept_costs
)
SELECT *
FROM dept_costs
WHERE dept_total > (SELECT avg FROM avg_cost)
ORDER BY dname;

Example of WITH with the function

The following test table must be created for the WITH operator examples.

DROP TABLE t1 PURGE;

CREATE TABLE t1 AS
SELECT 1 AS id
FROM dual
CONNECT BY level <= 1000000;

--Statistics collection on CTAS is no longer required in 12c,
--provided that the request is issued by a non-SYS user,
--EXEC DBMS_STATS.gather_table_stats (USER, 't1');

In this WITH statement, the declaration section can be used to define PL/SQL functions as shown below.

WITH 
FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
BEGIN
RETURN p_id;
END;
SELECT with_function(id)
FROM t1
WHERE rownum = 1

--Result of
WITH_FUNCTION(ID)
-----------------
1

In terms of function name resolution, the PL/SQL functions defined in the WITH declaration section have priority over objects with the same name defined at the schema level.

An example of WITH with the procedure

We can also define procedures in the WITH operator announcement section, even if they are not used.

WITH
PROCEDURE with_procedure(p_id IN NUMBER) IS
BEGIN
DBMS_OUTPUT.put_line('p_id=' || p_id);
END;
SELECT id
FROM t1
WHERE rownum = 1

--Result of
ID
----------
1

In fact, you would only put the procedure in a WITH statement if you planned to call the procedure from a function in the announcements section.

WITH
PROCEDURE with_procedure(p_id IN NUMBER) IS
BEGIN
DBMS_OUTPUT.put_line('p_id=' || p_id);
END;

FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
BEGIN
with_procedure(p_id);
RETURN p_id;
END;
SELECT with_function(id)
FROM t1
WHERE rownum = 1

--Result of
WITH_FUNCTION(ID)
-----------------
1

p_id=1

It seems that this function is not supported by PL/SQL. Any attempt to use it will result in compilation errors, as shown below.

BEGIN
FOR cur_rec IN (WITH
FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
BEGIN
RETURN p_id;
END;
SELECT with_function(id)
FROM t1
WHERE rownum = 1)
LOOP
NULL;
END LOOP;
END;

FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
*
ERROR at line 3:
ORA-06550: line 3, column 30:
PL/SQL: ORA-00905: missing keyword
ORA-06550: line 2, column 19:
PL/SQL: SQL Statement ignored
ORA-06550: line 5, column 34:
PLS-00103: Encountered the symbol ";" when expecting one of the following:
loop

Using dynamic SQL allows you to bypass this restriction.

SET SERVEROUTPUT ON
DECLARE
l_sql VARCHAR2(32767);
l_cursor SYS_REFCURSOR;
l_value NUMBER;
BEGIN .
l_sql := 'WITH
FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
BEGIN
RETURN p_id;
END;
SELECT with_function(id)
FROM t1
WHERE rownum = 1';

OPEN l_cursor FOR l_sql;
FETCH l_cursor INTO l_value;
DBMS_OUTPUT.put_line('l_value=' || l_value);
CLOSE l_cursor;
END;

l_value=1

PL/SQL procedure successfully completed.

Support for this function using static SQL within PL/SQL is expected in the next Oracle release.

PRODUCTIVITY BENEFITS

The whole reason to define embedded PL/SQL code is to improve performance.
Create a normal function to use as a comparison.

CREATE OR REPLACE FUNCTION normal_function(p_id IN NUMBER) RETURN NUMBER IS
BEGIN
RETURN p_id;
END;

Run the next test, which measures the time spent and CPU load of the query using the definition of the built-in function.

DECLARE
l_time PLS_INTEGER;
l_cpu PLS_INTEGER;

l_sql VARCHAR2(32767);
l_cursor SYS_REFCURSOR;

TYPE t_tab IS TABLE OF NUMBER;
l_tab t_tab;
BEGIN
l_time := DBMS_UTILITY.get_time;
l_cpu := DBMS_UTILITY.get_cpu_time;

l_sql := 'WITH
FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
BEGIN
RETURN p_id;
END;
SELECT with_function(id)
FROM t1';

OPEN l_cursor FOR l_sql;
FETCH l_cursor
BULK COLLECT INTO l_tab;
CLOSE l_cursor;

DBMS_OUTPUT.put_line('WITH_FUNCTION : ' ||.
'Time=' || TO_CHAR(DBMS_UTILITY.get_time - l_time) || ' hsecs ' ||
'CPU Time=' || (DBMS_UTILITY.get_cpu_time - l_cpu) || ' hsecs');

l_time := DBMS_UTILITY.get_time;
l_cpu := DBMS_UTILITY.get_cpu_time;

l_sql := 'SELECT normal_function(id)
FROM t1';

OPEN l_cursor FOR l_sql;
FETCH l_cursor
BULK COLLECT INTO l_tab;
CLOSE l_cursor;

DBMS_OUTPUT.put_line('NORMAL_FUNCTION: ' ||.
'Time=' || TO_CHAR(DBMS_UTILITY.get_time - l_time) || ' hsecs ' ||
'CPU Time=' || (DBMS_UTILITY.get_cpu_time - l_cpu) || ' hsecs');

END;

WITH_FUNCTION : Time=45 hsecs CPU Time=39 hsecs
NORMAL_FUNCTION: Time=129 hsecs CPU Time=113 hsecs

PL/SQL procedure successfully completed.

From this we see that the definition of the built-in function takes approximately one third of the time spent and the time of the processor for completion.

Example of PRAGMA UDF

In a number of presentations prior to the official release of 12c, speakers mentioned PRAGMA UDF (User Defined Function), which supposedly gives you the performance advantages of built-in PL/SQL, while allowing you to define a PL/SQL object outside of the SQL statement. The following code overrides the previous common function to use this pragma.

CREATE OR REPLACE FUNCTION normal_function(p_id IN NUMBER) RETURN NUMBER IS
PRAGMA UDF;
BEGIN .
RETURN p_id;
END;

Once the function is compiled, the execution of the test from the previous section for this function gives quite interesting results.

SET SERVEROUTPUT ON
DECLARE
l_time PLS_INTEGER;
l_cpu PLS_INTEGER;

l_sql VARCHAR2(32767);
l_cursor SYS_REFCURSOR;

TYPE t_tab IS TABLE OF NUMBER;
l_tab t_tab;
BEGIN
l_time := DBMS_UTILITY.get_time;
l_cpu := DBMS_UTILITY.get_cpu_time;

l_sql := 'WITH
FUNCTION with_function(p_id IN NUMBER) RETURN NUMBER IS
BEGIN
RETURN p_id;
END;
SELECT with_function(id)
FROM t1';

OPEN l_cursor FOR l_sql;
FETCH l_cursor
BULK COLLECT INTO l_tab;
CLOSE l_cursor;

DBMS_OUTPUT.put_line('WITH_FUNCTION : ' ||.
'Time=' || TO_CHAR(DBMS_UTILITY.get_time - l_time) || ' hsecs ' ||
'CPU Time=' || (DBMS_UTILITY.get_cpu_time - l_cpu) || ' hsecs');

l_time := DBMS_UTILITY.get_time;
l_cpu := DBMS_UTILITY.get_cpu_time;

l_sql := 'SELECT normal_function(id)
FROM t1';

OPEN l_cursor FOR l_sql;
FETCH l_cursor
BULK COLLECT INTO l_tab;
CLOSE l_cursor;

DBMS_OUTPUT.put_line('NORMAL_FUNCTION: ' ||.
'Time=' || TO_CHAR(DBMS_UTILITY.get_time - l_time) || ' hsecs ' ||
'CPU Time=' || (DBMS_UTILITY.get_cpu_time - l_cpu) || ' hsecs');

END;

WITH_FUNCTION : Time=44 hsecs CPU Time=40 hsecs
NORMAL_FUNCTION: Time=33 hsecs CPU Time=29 hsecs

PL/SQL procedure successfully completed.

It seems that a stand-alone function using the PRAGMA UDF consistently performs the built-in function.

I have the impression that calling a function defined with PRAGMA UDF directly from PL / SQL will not succeed. It doesn’t look like an accident.

DECLARE
l_number NUMBER;
BEGIN
l_number := normal_function(1);
END;

PL/SQL procedure successfully completed.

SQL: WITH Clause

 
Tags: , , , , ,

MORE NEWS

 

Preamble​​NoSql is not a replacement for SQL databases but is a valid alternative for many situations where standard SQL is not the best approach for...

Preamble​​MongoDB Conditional operators specify a condition to which the value of the document field shall correspond.Comparison Query Operators $eq...

5 Database management trends impacting database administrationIn the realm of database management systems, moreover half (52%) of your competitors feel...

The data type is defined as the type of data that any column or variable can store in MS SQL Server. What is the data type? When you create any table or...

Preamble​​MS SQL Server is a client-server architecture. MS SQL Server process starts with the client application sending a query.SQL Server accepts,...

First the basics: what is the master/slave?One database server (“master”) responds and can do anything. A lot of other database servers store copies of all...

Preamble​​Atom Hopper (based on Apache Abdera) for those who may not know is an open-source project sponsored by Rackspace. Today we will figure out how to...

Preamble​​MongoDB recently introduced its new aggregation structure. This structure provides a simpler solution for calculating aggregated values rather...

FlexibilityOne of the most advertised features of MongoDB is its flexibility.  Flexibility, however, is a double-edged sword. More flexibility means more...

Preamble​​SQLShell is a cross-platform command-line tool for SQL, similar to psql for PostgreSQL or MySQL command-line tool for MySQL.Why use it?If you...

Preamble​​Writing an application on top of the framework on top of the driver on top of the database is a bit like a game on the phone: you say “insert...

Preamble​​Oracle Coherence is a distributed cache that is functionally comparable with Memcached. In addition to the basic function of the API cache, it...

Preamble​​IBM pureXML, a proprietary XML database built on a relational mechanism (designed for puns) that offers both relational ( SQL / XML ) and...

  What is PostgreSQL array? In PostgreSQL we can define a column as an array of valid data types. The data type can be built-in, custom or enumerated....

Preamble​​If you are a Linux sysadmin or developer, there comes a time when you need to manage an Oracle database that can work in your environment.In this...

Preamble​​Starting with Microsoft SQL Server 2008, by default, the group of local administrators is no longer added to SQL Server administrators during the...