Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guidance on using the WITH clause in SQL

I understand how to use the WITH clause for recursive queries (!!), but I'm having problems understanding its general use / power.

For example the following query updates one record whose id is determined by using a subquery returning the id of the first record by timestamp:

update global.prospect psp
set    status=status||'*'
where  psp.psp_id=(
           select  p2.psp_id
           from    global.prospect p2
           where   p2.status='new' or p2.status='reset'
           order   by p2.request_ts
           limit   1 )
returning psp.*;

Would this be a good candidate for using a WITH wrapper instead of the relatively ugly sub-query? If so, why?

like image 848
cc young Avatar asked Jan 04 '12 02:01

cc young


People also ask

Can we use with clause in select statement?

The WITH clause allows you, as part of your select statement, to assign a name to a subquery and utilise its results by referencing that name. It is, on first glance, quite jarring. Because the subquery factoring clause brutally transforms the look of a query, making it no longer start with the SELECT keyword.

How do you write a two WITH clause in SQL?

To have multiple WITH clauses, you do not need to specify WITH multiple times. Rather, after the first WITH clause is completed, add a comma, then you can specify the next clause by starting with <query_name> followed by AS. There is no comma between the final WITH clause and the main SQL query.

Does with clause improve performance?

Oracle call's the WITH clause "sub-query factoring". Its main use is to improve the performance of queries which use the same sub-query more than once. We can also use it to make our code easier to understand but over-simplification can cause poor performance.

Can we use with clause in stored procedure?

WITH is not a standalone, it always a part of a whole statement and only one statement. It is not recognizable outside the scope ofits statement.


1 Answers

If there can be concurrent write access to involved tables, there are race conditions in the following queries. Consider:

  • Postgres UPDATE … LIMIT 1

Your example can use a CTE (common table expression), but it will give you nothing a subquery couldn't do:

WITH x AS (
   SELECT  psp_id
   FROM    global.prospect
   WHERE   status IN ('new', 'reset')
   ORDER   BY request_ts
   LIMIT   1
   )
UPDATE global.prospect psp
SET    status = status || '*'
FROM   x
WHERE  psp.psp_id = x.psp_id
RETURNING psp.*;

The returned row will be the updated version.


If you want to insert the returned row into another table, that's where a WITH clause becomes essential:

WITH x AS (
   SELECT  psp_id
   FROM    global.prospect
   WHERE   status IN ('new', 'reset')
   ORDER   BY request_ts
   LIMIT   1
   )
, y AS (
   UPDATE global.prospect psp
   SET    status = status || '*'
   FROM   x
   WHERE  psp.psp_id = x.psp_id
   RETURNING psp.*
   )
INSERT INTO z
SELECT *
FROM   y;

Data-modifying queries using CTEs were added with PostgreSQL 9.1.
The manual about WITH queries (CTEs).

like image 109
Erwin Brandstetter Avatar answered Sep 30 '22 14:09

Erwin Brandstetter