SQL Tutorial

SQL Clauses / Operators

SQL-Injection

SQL Functions

SQL Queries

PL/SQL

MySQL

SQL Server

Misc

SQL | WITH clause

The WITH clause, often referred to as Common Table Expressions (CTEs), provides a way to temporarily create a named result set that can be referred to within a main query. The WITH clause can be used for breaking down complex queries into simpler parts, allowing better readability and maintenance, and can also be used for recursive queries.

Syntax:

WITH cte_name (column_name1, column_name2, ...)
AS (
    -- Your subquery here
)
-- Main query using the CTE
SELECT ...
FROM cte_name
...

Basic Example:

Suppose you want to get the average salary from an employees table and then find out which employees earn more than the average.

Without CTE:

SELECT name, salary
FROM employees
WHERE salary > (
    SELECT AVG(salary) FROM employees
);

Using the WITH clause:

WITH AverageSalary AS (
    SELECT AVG(salary) AS avg_sal
    FROM employees
)
SELECT name, salary
FROM employees
WHERE salary > (SELECT avg_sal FROM AverageSalary);

Recursive CTEs:

One of the most powerful features of CTEs is their ability to be recursive. This is very useful for tasks like hierarchy traversal.

Consider a table hierarchy that has an id, parent_id, and name. Here's an example of a recursive CTE to find all descendants of a particular item:

WITH RECURSIVE Descendants AS (
    SELECT id, parent_id, name
    FROM hierarchy
    WHERE parent_id = [given_id]
    UNION ALL
    SELECT h.id, h.parent_id, h.name
    FROM hierarchy h
    INNER JOIN Descendants d ON h.parent_id = d.id
)
SELECT * FROM Descendants;

Advantages of Using CTEs:

  1. Readability: CTEs can make your query more readable by breaking it into modular blocks.
  2. Maintainability: Modular blocks mean that parts of the query can be changed without affecting other parts.
  3. Avoiding Repetitive Subqueries: If a subquery will be used in multiple places in the main query, a CTE can avoid redundancy.
  4. Recursive Queries: As shown above, CTEs can be used for recursive operations, which are hard to achieve without them.

Points to Remember:

  • CTEs are not materialized; they're essentially just named subqueries. Their results aren't stored anywhere.
  • The scope of a CTE is only the query in which it's defined. It's not available to subsequent queries unless redefined.

In summary, the WITH clause or CTEs provide a convenient way to structure and organize complex SQL queries, making them more readable and maintainable. They're especially useful for recursive tasks and for replacing repetitive subqueries.

  1. How to Use WITH in SQL:

    • Description: The WITH clause, also known as a Common Table Expression (CTE), is used to define a temporary result set that can be referred to within a SELECT, INSERT, UPDATE, or DELETE statement.
    • Code:
      -- Using WITH in SQL
      WITH temp_table AS (
          SELECT column1, column2
          FROM some_table
      )
      SELECT * FROM temp_table;
      
  2. Creating Common Table Expressions with WITH:

    • Description: A Common Table Expression is created using WITH to define a named temporary result set.
    • Code:
      -- Creating a CTE with WITH
      WITH employee_names AS (
          SELECT employee_id, first_name, last_name
          FROM employees
      )
      SELECT * FROM employee_names;
      
  3. Recursive Common Table Expressions in SQL:

    • Description: Recursive CTEs are used for hierarchical data and require the UNION or UNION ALL operator.
    • Code:
      -- Recursive CTE for hierarchical data
      WITH recursive_category AS (
          SELECT category_id, category_name, parent_category_id
          FROM categories
          WHERE parent_category_id IS NULL
          UNION ALL
          SELECT c.category_id, c.category_name, c.parent_category_id
          FROM categories c
          JOIN recursive_category rc ON c.parent_category_id = rc.category_id
      )
      SELECT * FROM recursive_category;
      
  4. WITH and SELECT, INSERT, UPDATE, DELETE Statements:

    • Description: CTEs can be used with various SQL statements, including SELECT, INSERT, UPDATE, and DELETE.
    • Code:
      -- Using CTE with SELECT
      WITH high_salary_employees AS (
          SELECT employee_id, first_name, last_name
          FROM employees
          WHERE salary > 80000
      )
      SELECT * FROM high_salary_employees;
      
      -- Using CTE with INSERT
      WITH new_employee AS (
          VALUES (101, 'John', 'Doe', 90000)
      )
      INSERT INTO employees (employee_id, first_name, last_name, salary)
      SELECT * FROM new_employee;
      
      -- Using CTE with UPDATE
      WITH salary_increase AS (
          SELECT employee_id, salary * 1.1 AS new_salary
          FROM employees
          WHERE department_id = 10
      )
      UPDATE employees
      SET salary = si.new_salary
      FROM salary_increase si
      WHERE employees.employee_id = si.employee_id;
      
  5. Aliasing Columns in WITH Clauses:

    • Description: Columns in a CTE can be aliased for better readability.
    • Code:
      -- Aliasing columns in CTE
      WITH emp AS (
          SELECT employee_id AS emp_id, first_name AS emp_first_name
          FROM employees
      )
      SELECT * FROM emp;
      
  6. WITH and JOIN Operations in SQL:

    • Description: CTEs can be used in conjunction with JOIN operations to simplify complex queries.
    • Code:
      -- Using CTE with JOIN
      WITH employee_department AS (
          SELECT e.employee_id, e.first_name, e.last_name, d.department_name
          FROM employees e
          JOIN departments d ON e.department_id = d.department_id
      )
      SELECT * FROM employee_department;
      
  7. Using Multiple WITH Clauses in a Query:

    • Description: Multiple CTEs can be defined in a single query, each separated by a comma.
    • Code:
      -- Using multiple CTEs in a query
      WITH cte1 AS (
          SELECT * FROM table1
      ),
      cte2 AS (
          SELECT * FROM table2
      )
      SELECT * FROM cte1
      JOIN cte2 ON cte1.id = cte2.id;
      
  8. Handling NULL Values in WITH Clauses:

    • Description: NULL values can be handled within a CTE using standard SQL functions.
    • Code:
      -- Handling NULL values in CTE
      WITH clean_data AS (
          SELECT COALESCE(column1, 'N/A') AS cleaned_column
          FROM table1
      )
      SELECT * FROM clean_data;
      
  9. Using WITH and Window Functions in SQL:

    • Description: CTEs can be combined with window functions for advanced analytical queries.
    • Code:
      -- Using CTE with window function
      WITH ranked_employees AS (
          SELECT employee_id, first_name, last_name,
                 RANK() OVER (ORDER BY salary DESC) AS salary_rank
          FROM employees
      )
      SELECT * FROM ranked_employees;
      
  10. WITH vs. Subqueries in SQL:

    • Description: CTEs and subqueries can achieve similar results, but CTEs offer better readability and reusability.
    • Code:
      -- Using CTE vs. subquery
      WITH high_salary_employees AS (
          SELECT employee_id, first_name, last_name
          FROM employees
          WHERE salary > 80000
      )
      SELECT * FROM high_salary_employees;
      
      -- Equivalent subquery
      SELECT employee_id, first_name, last_name
      FROM employees
      WHERE salary > 80000;
      
  11. Using WITH in Complex Queries:

    • Description: CTEs are particularly useful in complex queries to break down the logic into more manageable parts.
    • Code:
      -- Using CTE in a complex query
      WITH revenue_data AS (
          -- Some complex query logic here
      ),
      customer_data AS (
          -- Another complex query logic here
      )
      SELECT * FROM revenue_data
      JOIN customer_data ON revenue_data.customer_id = customer_data.customer_id;
      
  12. Practical Examples of Using WITH in SQL Queries:

    • Description: Practical examples of how CTEs can be applied to solve real-world problems.
    • Code:
      -- Practical example using CTE
      WITH employee_hierarchy AS (
          SELECT employee_id, manager_id, CONNECT_BY_ROOT employee_id AS top_manager_id
          FROM employees
          CONNECT BY PRIOR employee_id = manager_id
      )
      SELECT * FROM employee_hierarchy;