Keeping it simple and how to do multiple CTE in a query
You can have multiple CTE
s in one query, as well as reuse a CTE
:
WITH cte1 AS
(
SELECT 1 AS id
),
cte2 AS
(
SELECT 2 AS id
)
SELECT *
FROM cte1
UNION ALL
SELECT *
FROM cte2
UNION ALL
SELECT *
FROM cte1
Note, however, that SQL Server
may reevaluate the CTE
each time it is accessed, so if you are using values like RAND()
, NEWID()
etc., they may change between the CTE
calls.
Include multiple inline functions and multiple CTEs in a WITH clause in a single query
As ever for a "what is the syntax for ..." questions, you should refer to the official documentation.
The SELECT
syntax is
query_block ::=
with_clause ::=
plsql_declarations ::=
subquery_factoring_clause ::=
The PL/SQL function syntax
function_definition ::=
body ::=
Therefore:
A PL/SQL function's body must be terminated with a
;
.(Note: this is a PLSQL statement terminator and not a separator in the
WITH
clause between PL/SQL function declarations as there is no separator character following PL/SQL function declarations.)There is a
,
character between successive sub-query factoring clauses.The
SELECT
statement does not need a;
or/
statement terminator but it may be allowed/required/forbidden by the client application you are using to denote the termination of the statement.For example:
- You can only pass a single statement via an OJDBC statement and, for this client, the statement terminator is forbidden.
- In SQL Developer, when you are running a single statement then the trailing statement terminator is allowed but is optional.
- In SQL Developer, when you are running a script then statement terminators are required between statements.
multiple cte in single select statement where ctes can refer to each other
Yes, you can reference previously declared CTEs in subsequent CTEs:
WITH cte1 as (
SELECT t.*
FROM cdr.Location t),
cte2 as (
SELECT t.*
FROM cdr.Location t
JOIN cte1 c1 ON c1.somefield = t.someField)
SELECT *
FROM cte1
UNION
SELECT *
FROM cte2
NOTES
- Joining onto cte2 in the cte1 declaration wouldn't work, because the statement is executed from top down.
- You reference a CTE like any other inline view (which it is) or table/temp table/etc by JOINing on whatever you need.
BTW: Try to formulate a better example in the future - it's good for you and the rest of the SO community who are trying to help you.
SQL export multiple CTE tables to a new table
Why can't I query all of my cte tables at once? They don't depend on each others.
Consider these things:
- While a single query can have multiple CTEs, a single CTE cannot be shared by multiple queries.
- I agree this is annoying.
- Each top-level
SELECT
statement represents a completely separate query.- (Confusingly, CTEs (
WITH x AS ( ... )
) are placed before theSELECT
keyword, however it is still a single query. - Therefore your
SELECT * FROM level1 SELECT * FROM level2
statements are two separate queries.- Only the first query (
SELECT * FROM level1
) has access to thelevel1
andlevel2
CTEs defined before it.
- Only the first query (
- This is why you should always use a terminating semicolon so you can not-only visually see the syntactical bounds of each statement and query, but also prevent you from making this kind of mistake again in future.
- So it should have been formatted as:
------------------
-- Query 1:
------------------
WITH level1 AS (
SELECT [order] FROM test
),
level2 AS AS (
SELECT [route], [tag] FROM test
)
SELECT * FROM level1;
------------------
-- Query 2 (which is *entirely separate* from Query 1 above):
------------------
SELECT * FROM level2; /* <-- Error: The `level2` CTE isn't in-scope! */
- So it should have been formatted as:
- (Confusingly, CTEs (
- You can combine two or more
SELECT
queries into one by concatenating their results usingUNION ALL
, however you can only do this for queries with the same column-design, but your twoSELECT
statements have different columns ([order]
) vs ([route]
and[tag]
) so it's nonsensical to concatenate them as-is.
But if you do want to concatenate them, using NULL
for their missing columns' data, then do this:
WITH level1 AS (
SELECT
[order]
FROM
test
),
level2 AS AS (
SELECT
[route],
[tag]
FROM
test
)
SELECT
NULL AS [route],
NULL AS [tag],
[order]
FROM
level1
UNION ALL
SELECT
[route],
[tag]
NULL AS [order]
FROM
level2; /* <-- Note the semicolon. */
Use Multiple CTE
The first one fails because a CTE or set of CTEs can only be followed by a single statement.
You could rewrite it as
; with [cteOne] as (
select 1 as col
)
select 'yesA' where exists (select * from [cteOne])
; with [cteTwo] as (
select 2 as col
)
select 'yexB' where exists (select * from [cteTwo])
Use one CTE many times
A CTE
is basically a disposable view. It only persists for a single statement, and then automatically disappears.
Your options include:
Redefine the
CTE
a second time. This is as simple as copy-paste fromWITH...
through the end of the definition to before yourSET
.Put your results into a
#temp
table or a@table
variableMaterialize the results into a real table and reference that
Alter slightly to just
SELECT COUNT
from your CTE:
.
SELECT @total = COUNT(*)
FROM Players p
INNER JOIN Teams t
ON p.IdTeam=t.Id
INNER JOIN Leagues l
ON l.Id=t.IdLeague
WHERE l.Id=@idleague
Related Topics
Postgres Window Function and Group by Exception
Replacing Null with 0 in a SQL Server Query
SQL (Oracle): Order by and Limit
How to Delete from Select in MySQL
Does MySQL Have an Equivalent to @@Rowcount Like in Mssql
SQL "Select Where Not in Subquery" Returns No Results
How to Use SQL's Getdate() and Dateadd() in a Linq to SQL Expression
Select Top X (Or Bottom) Percent for Numeric Values in MySQL
SQL Selecting Rows by Most Recent Date with Two Unique Columns
SQL Group by Case Statement with Aggregate Function
Calculate Working Hours Between 2 Dates in Postgresql
How to Return Rows with a Specific Value First
How to Calculate a Running Total in SQL Without Using a Cursor
Does Ms SQL Server's "Between" Include the Range Boundaries
How to Run a Stored Procedure in SQL Server Every Hour
Extract Date (Yyyy/Mm/Dd) from a Timestamp in Postgresql
What's the Best to Check If Item Exist or Not: Select Count(Id)Or Exist(...)