• Pavol Drotován
  • DB expert
  • 02-05-2023

Common table expression(CTE) - Postgres

Common table expression (CTE) is among the extended elements of every database system, including postgres. It represents a temporary SQL query that can be used multiple times within a longer and complex selection, the sql is more readable and the script is faster and more optimized. In postgres, you can also delete and update based on the CTE result. The CTE structure looks like this:

WITH RECURSIVE cte_name (column1, column2, ...) AS( -- anchor member SELECT select_list FROM table1 WHERE condition UNION [ALL] -- recursive term SELECT select_list FROM cte_name WHERE recursive_condition ) SELECT * FROM cte_name;

It follows from the above that each CTE must start with the keyword WITH followed by the name of the CTE (if we want a repeating CTE, we must put the word "RECURSIVE"), followed by AS and select is used in the parenthesis, then the parenthesis is closed if we want to add more CTE, a comma and the name of the CTE are added after the closed parenthesis, and again the script in parentheses:

WITH RECURSIVE cte_name (column1, column2, ...) AS( -- anchor member SELECT select_list FROM table1 WHERE condition ), cte_name2 (column1, column2, ...) AS( -- anchor member SELECT select_list FROM table1 WHERE condition ) SELECT * FROM cte_name union all SELECT * FROM cte_name2 ;

If we want to record the number of deleted lines or changed e.g. to some logging table called log_table, we can use CTE:

--delete WITH MOVED_ROWS AS (DELETE FROM TABLE_NAME RETURNING *) INSERT INTO LOG_TABLE SELECT MR.*, CURRENT_TIMESTAMP FROM MOVED_ROWS MR; - update WITH CTE AS (UPDATE TABLE_NAME SET COLUMN_NAME_NUMERIC = 100 RETURNING *) INSERT INTO LOG_TABLE SELECT * FROM CTE ;

Another possibility is the use of CTE in Recursive query, when the repeating script in CTE can go through several levels and thus record all the records in the resulting query. find out who is whose manager and what the sequence is.

create table employees ( employee_id numeric(6) not null constraint emp_emp_id_pk primary key, first_name varchar(20), last_name varchar(25) not null, email varchar(25) not null, phone_numeric varchar(20), hire_date date not null, job_id varchar(10) not null, salaries numeric(8, 2), commission_pct numeric(2, 2), manager_id numeric(6) constraint emp_manager_fk reference employees, department_id numeric(4) ); insert into public.employees (employee_id, first_name, last_name, email, phone_numeric, hire_date, job_id, salary, commission_pct, manager_id, department_id) values (100, 'Steven', 'King', 'SKING', '515.123.4567', '2003-06-17', 'AD_PRES', 24000.00, null, null, 90), (101, 'Neena', 'Kochhar', 'NKOCHHAR', '515.123.4568', '2005-09-21', 'AD_VP', 17000.00, null, 100, 90), (102, 'Lex', 'De Haan', 'LDEHAAN', '515.123.4569', '2001-01-13', 'AD_VP', 15000.00, null, 100, 90), (114, 'Den', 'Raphaely', 'DRAPHEAL', '515.127.4561', '2002-12-07', 'PU_MAN', 11000.00, null, 100, 30), (120, Matthew, Weiss, MWEISS, 650.123.1234, 2004-07-18, ST_MAN, 8000.00, null, 100, 50), (121, 'Adam', 'Fripp', 'AFRIPP', '650.123.2234', '2005-04-10', 'ST_MAN', 8200.00, null, 100, 50), (122, 'Payam', 'Kaufling', 'PKAUFLIN', '650.123.3234', '2003-05-01', 'ST_MAN', 7900.00, null, 100, 50), (123, 'Shanta', 'Vollman', 'SVOLLMAN', '650.123.4234', '2005-10-10', 'ST_MAN', 6500.00, null, 100, 50), (124, 'Kevin', 'Mourgos', 'KMOURGOS', '650.123.5234', '2007-11-16', 'ST_MAN', 5800.00, null, 100, 50), (145, 'John', 'Russell', 'JRUSSEL', '011.44.1344.429268', '2004-10-01', 'SA_MAN', 14000.00, 0.40, 100, 80), (146, 'Karen', 'Partners', 'KPARTNER', '011.44.1344.467268', '2005-01-05', 'SA_MAN', 13500.00, 0.30, 100, 80), (147, 'Alberto', 'Errazuriz', 'AERRAZUR', '011.44.1344.429278', '2005-03-10', 'SA_MAN', 12000.00, 0.30, 100, 8 0), (148, 'Gerald', 'Cambrault', 'GCAMBRAU', '011.44.1344.619268', '2007-10-15', 'SA_MAN', 11000.00, 0.30, 100, 80 ), (149; (201, 'Michael', 'Hartstein', 'MHARTSTE', '515.123.5555', '2004-02-17', 'MK_MAN', 13000.00, null, 100, 20); WITH RECURSIVE EMPLOYEE_REC AS (SELECT first_name,last_name,employee_id,'manager' as to FROM employees with where employee_id=100 UNION SELECT e.first_name,e.last_name,e.manager_id,'subordinates' a.s. no FROM employees e INNER JOIN EMPLOYEE_REC S ON S.employee_id = E.manager_id) SELECT * FROM EMPLOYEES_REC;

Using examples, we showed the use of CTE in the database tool Postgres. The basic idea is not to repeat the query unnecessarily multiple times in the script and thus reduce I/O operations and to have a temporary script to which the following scripts follow. At the same time, it is a solution that does not allocate any memory compared to temp tables and has more possibilities of use. It is possible to create a logging table or make a recursive query, which is an advantage of CTE in postgres compared to other database tools (Oracle, MS SERVER) which perform the given operations through complex functions and procedures. I recommend using CTE every day for everyone and thus write more optimized scripts.