(Common Table Expressions) CTE as part of WHERE clause... possible? - sql

Is it possible to use CTE in a WHERE clause e.g.
SELECT *
FROM Table1
WHERE Table1.PK IN (
WITH Cte AS (
-- root selection (dynamic, generated in code)
SELECT Bla FROM Table2
-- recursive part
UNION ALL
SELECT …..)
SELECT Bla FROM Cte)
The reason I’m asking is that I need to use a recursive query and the only way of doing it at the moment without updating our framework is to place it in the where clause.

No, WITH clauses need to be defined before the main SELECT. Like this:
WITH recursive_cte AS (
-- root selection (dynamic, generated in code)
SELECT Bla FROM Table2
-- recursive part
UNION ALL
SELECT …..)
SELECT t.*
FROM TABLE1 t
JOIN recursive_cte rc ON rc.key = t.pk
I also tweaked the query to use a JOIN instead, but you'll have to watch for duplicates.

Yes you can apply WHERE CLAUSE with CTE. You have to make Table-Valued Function and return result in Table.
then you can use this result set in any query with WHERE Clause.
Go to the link and find example:
http://muhammadnaveed.info/use-cte-query-in-where-clause/
Hope this will help you.

Related

Use recursive on temp. view with "with" [duplicate]

Is it possible to combine multiple CTEs in single query?
I am looking for way to get result like this:
WITH cte1 AS (
...
),
WITH RECURSIVE cte2 AS (
...
),
WITH cte3 AS (
...
)
SELECT ... FROM cte3 WHERE ...
As you can see, I have one recursive CTE and two non recursive.
Use the key word WITH once at the top. If any of your Common Table Expressions (CTE) are recursive (rCTE) you have to add the keyword RECURSIVE at the top once also, even if not all CTEs are recursive:
WITH RECURSIVE
cte1 AS (...) -- can still be non-recursive
, cte2 AS (SELECT ...
UNION ALL
SELECT ...) -- recursive term
, cte3 AS (...)
SELECT ... FROM cte3 WHERE ...
The manual:
If RECURSIVE is specified, it allows a SELECT subquery to
reference itself by name.
Bold emphasis mine. And, even more insightful:
Another effect of RECURSIVE is that WITH queries need not be ordered:
a query can reference another one that is later in the list. (However,
circular references, or mutual recursion, are not implemented.)
Without RECURSIVE, WITH queries can only reference sibling WITH
queries that are earlier in the WITH list.
Bold emphasis mine again. Meaning that the order of WITH clauses is meaningless when the RECURSIVE key word has been used.
BTW, since cte1 and cte2 in the example are not referenced in the outer SELECT and are plain SELECT commands themselves (no collateral effects), they are never executed (unless referenced in cte3).
Yes. You don't repeat the WITH. You just use a comma:
WITH cte1 AS (
...
),
cte2 AS (
...
),
cte3 AS (
...
)
SELECT ... FROM 'cte3' WHERE ...
And: Only use single quotes for string and date constants. Don't use them for column aliases. They are not allowed for CTE names anyway.
As the accepted answer correctly says, the with clause is used only once per a CTE chain. However, for sake of completeness, I would like to add it does not stop you from nesting CTEs.
If cte2 uses cte1, cte3 uses cte2 etc., then the dependency chain between CTEs is linear and it is expressed as with with 3 CTEs. On the contrary, if cte2 doesn't need cte1 and both are needed only in cte3 it should be considered to nest them under definition of cte3 (with cte3 as (with cte1 as (...), cte2 as (...) select...)).
The syntax of CTEs then reflects the dependency tree between CTEs and literally visualizes the scope of partial datasets which can improve readability and prevents scope leakage bugs. Not all db vendors support it but Postgres does.
Example:
with cte1(id,capital) as (
values(1,'Prague'),(2,'Bratislava')
), cte2(id,code) as (
with cte2inner1(id,code) as (
values(1,'CZ'),(2,'SK')
), cte2inner2(id,country) as (
values(1,'Czech Republic'),(2,'Slovakia')
)
select id,country from cte2inner1 join cte2inner2 using (id)
)
select *
from cte1 join cte2 using (id)
--join cte2inner1 not possible here
Problem Reason: Here, you don't have to use multiple WITH clause for combine Multiple CTE.
Solution: It is possible to create the Multiple Common Table Expression's using single WITH clause in SQL. The two different CTE's are created using Single WITH Clause and this is separated by comma to create multiple CTE's.
Sample Multiple CTE's using single
With EmpCount1(DeptName,TotalEmployees)
as
(
Select DeptName, COUNT(*) as TotalEmployees
from Tbl_EmpDetails
join Tbl_Dept Dept
on Tbl_EmpDetails.DeptId = Dept.DeptId
WHERE DeptName IN ('BI','DOTNET')
group by DeptName
),
EmpCount2(DeptName,TotalEmployees)
as
(
Select DeptName, COUNT(*) as TotalEmployees
from Tbl_EmpDetails
join Tbl_Dept Dept
on Tbl_EmpDetails.DeptId = Dept.DeptId
WHERE DeptName IN ('JAVA','AI')
group by DeptName
)
Select * from EmpCount1
UNION
Select * from EmpCount2
This is sample syntax for creating multiple Common Table Expression's with a single With Clause.

Assign a value to a variable inside a CTE

I want to assign a value to my variable inside a CTE and use it in my next CTE.
Is it possible?
declare #TypeQty as int
;
With CTE1 as
(#TypeQty = (select Count(ID) as MyQty from MyTbl))
,CTE2 as
(select * from MyTbl2 where Qty= #TypeQty)
Select * from CTE2
I know it possible with some way else, but I want to know is it possible with CTE or not?
In SQL Server you can't use this syntax as you wrote in the question.
You can use the calculated value without the variable, just by referencing the CTE itself. Something like this:
WITH
CTE1
as
(
select Count(ID) as MyQty
from MyTbl
)
,CTE2
as
(
select *
from
MyTbl2
INNER JOIN CTE1 ON MyTbl2.Qty = CTE1.MyQty
)
Select *
from CTE2
;
This is explicitly not allowed in SQL Server. A query either returns a result set or (exclusively) assigns variables, but not both.
I do wonder why you would use either CTEs or variables for this example. A simple subquery seems simple enough:
Select t2.*
from MyTbl2 t2
where t2.qty = (select count(*) from mytable t);
CTEs in SQL Server do not offer performance benefits. And I don't see any "clarity-of-code" benefits to using them in this case.
No, it's impossible to populate variables inside a cte query definition.
The cte query definition must return a result set, and it's impossible to do both in T-SQL - a select statement can either return a result set or populate variables - but not both at the same time.
Therefor, it's impossible to populate variable inside a common table expression query.
You can use SUBQUERY Instead of using Recursive CTE
SELECT * FROM MYTBL2 M
INNER JOIN(
SELECT COUNT(ID) AS MYQTY FROM MYTBL) S ON M.QTY=S.MYQTY

Remove duplicate sub-query

I have a complex SQL query that can be simplified to the below:
Select ColA,ColB,ColC,ColD
From MyTable
Where (ColA In (Select ItemID From Items Where ItemName like '%xxx%')
or ColB In (Select ItemID From Items Where ItemName like '%xxx%'))
As you can see, the sub-query appears twice. Is the compiler intelligent enough to detect this and gets the result of the sub-query only once? Or does the sub-query run twice?
FYI, table Items has about 20,000 rows and MyTable has about 200,000 rows.
Is there another way to re-write this SQL statement so that the sub-query appears/runs only once?
Update: The Where clause in the main query is dynamic and added only when needed (i.e. only when a user searches for 'xxx'). Hence changes to the main select statement or re-structuring of the query are not possible.
UPDATE Your request not to change the query, just the WHERE
You can pack the CTE directly in the place where it is called (untested):
Select ColA,ColB,ColC,ColD
From MyTable
Where EXISTS (SELECT 1 FROM (Select i.ItemID
From Items AS i
Where iItemName like '%xxx%') AS itm
WHERE itm.ItemID=MyTable.ColA OR itm.ItemID=MyTable.ColB)
previous
I think this should be the same...
WITH MyCTE AS
(
Select ItemID From Items Where ItemName like '%xxx%'
)
Select ColA,ColB,ColC,ColD
From MyTable
Where EXISTS (SELECT 1 FROM MyCTE WHERE ItemID=ColA OR ItemID=ColB)
A substring LIKE search is - for sure - not performant.
If you can reduce your "Items" to just a few rows with your LIKE filter, you must test which is fastest.
You can also write the query like this:
SELECT ColA, ColB, ColC, ColD
FROM MyTable
WHERE EXISTS(
(SELECT ItemID FROM Items WHERE ItemName LIKE '%xxx%')
INTERSECT
SELECT t.v FROM (VALUES (ColA), (ColB)) AS t(v) )
There is no guarantee that it will follow the actual execution order. It depends on how you write a query. Identical subqueries are normally only performed once.
There is a WITH clause in standard SQL.
WITH mySubQuery AS
(
[the subquery code]
)
SELECT * FROM
mySubQuery AS sq
WHERE xyz IN (mySubQuery)
SQL programmers can use CTE (Common Table Expression) in such cases
You can define a CTE using the sub-query once and use it in the SQL statement by referencing more than once.
Please refer to SQL CTE Common Table Expression tutorial for samples
CTE's are very powerful tools for SQL developers especially when used as recursive-queries

How to use multiple CTEs in a single SQL query?

Is it possible to combine multiple CTEs in single query?
I am looking for way to get result like this:
WITH cte1 AS (
...
),
WITH RECURSIVE cte2 AS (
...
),
WITH cte3 AS (
...
)
SELECT ... FROM cte3 WHERE ...
As you can see, I have one recursive CTE and two non recursive.
Use the key word WITH once at the top. If any of your Common Table Expressions (CTE) are recursive (rCTE) you have to add the keyword RECURSIVE at the top once also, even if not all CTEs are recursive:
WITH RECURSIVE
cte1 AS (...) -- can still be non-recursive
, cte2 AS (SELECT ...
UNION ALL
SELECT ...) -- recursive term
, cte3 AS (...)
SELECT ... FROM cte3 WHERE ...
The manual:
If RECURSIVE is specified, it allows a SELECT subquery to
reference itself by name.
Bold emphasis mine. And, even more insightful:
Another effect of RECURSIVE is that WITH queries need not be ordered:
a query can reference another one that is later in the list. (However,
circular references, or mutual recursion, are not implemented.)
Without RECURSIVE, WITH queries can only reference sibling WITH
queries that are earlier in the WITH list.
Bold emphasis mine again. Meaning that the order of WITH clauses is meaningless when the RECURSIVE key word has been used.
BTW, since cte1 and cte2 in the example are not referenced in the outer SELECT and are plain SELECT commands themselves (no collateral effects), they are never executed (unless referenced in cte3).
Yes. You don't repeat the WITH. You just use a comma:
WITH cte1 AS (
...
),
cte2 AS (
...
),
cte3 AS (
...
)
SELECT ... FROM 'cte3' WHERE ...
And: Only use single quotes for string and date constants. Don't use them for column aliases. They are not allowed for CTE names anyway.
As the accepted answer correctly says, the with clause is used only once per a CTE chain. However, for sake of completeness, I would like to add it does not stop you from nesting CTEs.
If cte2 uses cte1, cte3 uses cte2 etc., then the dependency chain between CTEs is linear and it is expressed as with with 3 CTEs. On the contrary, if cte2 doesn't need cte1 and both are needed only in cte3 it should be considered to nest them under definition of cte3 (with cte3 as (with cte1 as (...), cte2 as (...) select...)).
The syntax of CTEs then reflects the dependency tree between CTEs and literally visualizes the scope of partial datasets which can improve readability and prevents scope leakage bugs. Not all db vendors support it but Postgres does.
Example:
with cte1(id,capital) as (
values(1,'Prague'),(2,'Bratislava')
), cte2(id,code) as (
with cte2inner1(id,code) as (
values(1,'CZ'),(2,'SK')
), cte2inner2(id,country) as (
values(1,'Czech Republic'),(2,'Slovakia')
)
select id,country from cte2inner1 join cte2inner2 using (id)
)
select *
from cte1 join cte2 using (id)
--join cte2inner1 not possible here
Problem Reason: Here, you don't have to use multiple WITH clause for combine Multiple CTE.
Solution: It is possible to create the Multiple Common Table Expression's using single WITH clause in SQL. The two different CTE's are created using Single WITH Clause and this is separated by comma to create multiple CTE's.
Sample Multiple CTE's using single
With EmpCount1(DeptName,TotalEmployees)
as
(
Select DeptName, COUNT(*) as TotalEmployees
from Tbl_EmpDetails
join Tbl_Dept Dept
on Tbl_EmpDetails.DeptId = Dept.DeptId
WHERE DeptName IN ('BI','DOTNET')
group by DeptName
),
EmpCount2(DeptName,TotalEmployees)
as
(
Select DeptName, COUNT(*) as TotalEmployees
from Tbl_EmpDetails
join Tbl_Dept Dept
on Tbl_EmpDetails.DeptId = Dept.DeptId
WHERE DeptName IN ('JAVA','AI')
group by DeptName
)
Select * from EmpCount1
UNION
Select * from EmpCount2
This is sample syntax for creating multiple Common Table Expression's with a single With Clause.

How to join two equivalent tables which are the result of the previous recursive select in SQL Server

Good day everyone! Firstly, I'm sorry for my poor english. Well, I've got a question that you can read in the title of this message.
SQL Server returns this message(Error 253) when I'm trying to select necessary data.
Translate "Recursive element from CTE (which name is 'recurse' - my
note) has multiple reference in CTE.
How can I solve this problem?
Can you advice me how to join two tables (with 2 columns(for example : a and b) which are the result of previous recursive select (I'm writing about the same select, but about another iteration of if)
with recurse (who_acts,on_whom_influence)
as
(
-------------------------------------------FIRST SELECT
select distinct interface_1.robot_name as who_acts,interface_2.robot_name as on_whom_influence
from INTERFACE as interface_1,INTERFACE as interface_2
where (interface_1.number in ( select INPUT_INTERFACE.source
from INPUT_INTERFACE
)
and interface_2.number in (
select INPUT_INTERFACE.number
from INPUT_INTERFACE
where (INPUT_INTERFACE.source=interface_1.number )
)
)
-------------------------------------------RECURSIVE PART
union all
select rec1.who_acts,rec1.on_whom_influence
from recurse as rec1
inner join
(select rec2.who_acts,rec2.on_whom_influence
from recurse as rec2) as P on (1=1)
)
select * from recurse
The problem is in recurse CTE.The connecting condition is not simple, but it have no
influence on this problem.
Can you type some working code in comments
Here's a dummy table
create table tbl1 ( a int, b int );
insert tbl1 select 1,2;
insert tbl1 select 11,12;
insert tbl1 select 2,3;
insert tbl1 select 4,5;
And a similar query to yours
with cte as (
select a,b from tbl1
union all
select x.a,x.b from cte x join cte y on x.a=y.a+1
)
select * from cte;
The error:
Recursive member of a common table expression 'cte' has multiple recursive references.: with cte as ( select a,b from tbl1 union all select x.a,x.b from cte x join cte y on x.a=y.a+1 ) select * from cte
Basically, the error is exactly what it says. You cannot have a recursive CTE appear more than ONCE in a recursive section. Above, you see CTE aliased as both x and y. There are various reasons for this limitation, such as the fact that CTEs are recursed depth-first and not generation-by-generation.
What you should think about is why you would need it more than once. Your recursive portion doesn't make sense.
select rec1.who_acts,rec1.on_whom_influence
from recurse as rec1
inner join
( select rec2.who_acts,rec2.on_whom_influence
from recurse as rec2) as P on (1=1)
On the surface, the following are true if recurse were a real table (non-CTE):
The number of rows generated is count(recurse as [rec1]) x count(recurse as [rec2]).
The rows in recurse (rec1) are each replicated per row in recurse, hence #1
Columns from rec2 are never used. rec2 serves only to multiply
If this were permitted to run, the recursive portion of the query would keep quadratically increasing its number of rows and never finish.