226
WITH y AS (
    WITH x AS (
        SELECT * FROM MyTable
    )
    SELECT * FROM x
)
SELECT * FROM y

Does something like this work? I tried it earlier but I couldn't get it to work.

spender
  • 117,338
  • 33
  • 229
  • 351
Joe Phillips
  • 49,743
  • 32
  • 103
  • 159

7 Answers7

373

While not strictly nested, you can use common table expressions to reuse previous queries in subsequent ones.

To do this, the form of the statement you are looking for would be

WITH x AS 
(
    SELECT * FROM MyTable
), 
y AS 
(
    SELECT * FROM x
)
SELECT * FROM y
spender
  • 117,338
  • 33
  • 229
  • 351
  • 2
    Thank you very much. I was able to do it in Oracle: WITH J AS (SELECT 1 AS ONE FROM DUAL), Q AS (SELECT J.*, 2 AS TWO FROM J) SELECT * FROM Q – Jason TEPOORTEN Jul 29 '13 at 01:35
  • 6
    this is not nested – symbiont Dec 30 '16 at 08:07
  • Yes, this is not nested, just a second CTE referencing a first CTE – Reversed Engineer May 15 '17 at 15:36
  • 21
    Essentially the post means, that *you can't do it*, but it is not a big problem. – peterh May 30 '17 at 14:40
  • 2
    Yes, this is an acceptable answer because what I was trying to achieve with nesting is the same thing this ends up giving me anyway – Joe Phillips Jun 02 '17 at 18:05
  • 4
    Stating that this is not nested, just because query 2 is not inside the parenthesis of query 1, sounds like a weak argument. I think it's nested (not recursively nested), because query 2 uses the result of query 1, which occurs with nesting too. Is defined that nesting can only be when a child is within its parent parenthesis (or similar) symbols? – Christiaan Westerbeek Sep 10 '18 at 19:56
  • 2
    It’s more akin to chaining, but whatever. It gets the job done and solved a similar issue for me in a simple, readable way. – Stonetip Mar 15 '20 at 12:38
17

You can do the following, which is referred to as a recursive query:

WITH y
AS
(
  SELECT x, y, z
  FROM MyTable
  WHERE [base_condition]

  UNION ALL

  SELECT x, y, z
  FROM MyTable M
  INNER JOIN y ON M.[some_other_condition] = y.[some_other_condition]
)
SELECT *
FROM y

You may not need this functionality. I've done the following just to organize my queries better:

WITH y 
AS
(
  SELECT * 
  FROM MyTable
  WHERE [base_condition]
),
x
AS
(
  SELECT * 
  FROM y
  WHERE [something_else]
)
SELECT * 
FROM x
David Andres
  • 31,351
  • 7
  • 46
  • 36
  • Awesome suggestion with putting main SELECT into next WITH clause and then SELECT from last WITH query. Thank You :) – Teamothy Nov 16 '21 at 09:31
9

With does not work embedded, but it does work consecutive

;WITH A AS(
...
),
B AS(
...
)
SELECT *
FROM A
UNION ALL
SELECT *
FROM B

EDIT Fixed the syntax...

Also, have a look at the following example

SQLFiddle DEMO

Adriaan Stander
  • 162,879
  • 31
  • 289
  • 284
0

we can create nested cte.please see the below cte in example

;with cte_data as 
(
Select * from [HumanResources].[Department]
),cte_data1 as
(
Select * from [HumanResources].[Department]
)

select * from cte_data,cte_data1
Werner Henze
  • 16,404
  • 12
  • 44
  • 69
0

I was trying to measure the time between events with the exception of what one entry that has multiple processes between the start and end. I needed this in the context of other single line processes.

I used a select with an inner join as my select statement within the Nth cte. The second cte I needed to extract the start date on X and end date on Y and used 1 as an id value to left join to put them on a single line.

Works for me, hope this helps.

cte_extract
as 
(
    select ps.Process as ProcessEvent
        , ps.ProcessStartDate 
        , ps.ProcessEndDate 
        -- select strt.*
    from dbo.tbl_some_table ps 
    inner join (select max(ProcessStatusId) ProcessStatusId 
                    from dbo.tbl_some_table 
                where Process = 'some_extract_tbl' 
                and convert(varchar(10), ProcessStartDate, 112) < '29991231'
                ) strt on strt.ProcessStatusId = ps.ProcessStatusID
), 
cte_rls
as 
(
    select 'Sample' as ProcessEvent, 
     x.ProcessStartDate, y.ProcessEndDate  from (
    select 1 as Id, ps.Process as ProcessEvent
        , ps.ProcessStartDate 
        , ps.ProcessEndDate
        -- select strt.*
    from dbo.tbl_some_table ps 
    inner join (select max(ProcessStatusId) ProcessStatusId 
                    from dbo.tbl_some_table 
                where Process = 'XX Prcss' 
                and convert(varchar(10), ProcessStartDate, 112) < '29991231'
                ) strt on strt.ProcessStatusId = ps.ProcessStatusID
    ) x
    left join (
        select 1 as Id, ps.Process as ProcessEvent
            , ps.ProcessStartDate 
            , ps.ProcessEndDate
            -- select strt.*
        from dbo.tbl_some_table ps 
        inner join (select max(ProcessStatusId) ProcessStatusId
                    from dbo.tbl_some_table 
                    where Process = 'YY Prcss Cmpltd' 
                    and convert(varchar(10), ProcessEndDate, 112) < '29991231'
                    ) enddt on enddt.ProcessStatusId = ps.ProcessStatusID
            ) y on y.Id = x.Id 
),

.... other ctes

natur3
  • 236
  • 1
  • 3
  • 14
0

Nested 'With' is not supported, but you can always use the second With as a subquery, for example:

WITH A AS (
                --WITH B AS ( SELECT COUNT(1) AS _CT FROM C ) SELECT CASE _CT WHEN 1 THEN 1 ELSE 0 END FROM B --doesn't work
                SELECT CASE WHEN count = 1 THEN 1 ELSE 0 END AS CT FROM (SELECT COUNT(1) AS count FROM dual)
                union all
                select 100 AS CT from dual
           )
              select CT FROM A
KOBER
  • 59
  • 1
  • 6
-1

These answers are pretty good, but as far as getting the items to order properly, you'd be better off looking at this article http://dataeducation.com/dr-output-or-how-i-learned-to-stop-worrying-and-love-the-merge

Here's an example of his query.

WITH paths AS ( 
    SELECT 
        EmployeeID, 
        CONVERT(VARCHAR(900), CONCAT('.', EmployeeID, '.')) AS FullPath 
    FROM EmployeeHierarchyWide 
    WHERE ManagerID IS NULL

    UNION ALL

    SELECT 
        ehw.EmployeeID, 
        CONVERT(VARCHAR(900), CONCAT(p.FullPath, ehw.EmployeeID, '.')) AS FullPath 
    FROM paths AS p 
        JOIN EmployeeHierarchyWide AS ehw ON ehw.ManagerID = p.EmployeeID 
) 
SELECT * FROM paths order by FullPath
Community
  • 1
  • 1
Don Rolling
  • 2,301
  • 4
  • 30
  • 27
  • 1
    My original question never said anything about unioning data together. It could have just as easily been joining data – Joe Phillips Mar 24 '16 at 19:50