Assign a value to a variable inside a CTE - sql

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

Related

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.

SQL alias for SELECT statement

I would like to do something like
(SELECT ... FROM ...) AS my_select
WHERE id IN (SELECT MAX(id) FROM my_select GROUP BY name)
Is it possible to somehow do the "AS my_select" part (i.e. assign an alias to a SELECT statement)?
(Note: This is a theoretical question. I realize that I can do it without assign an alias to a SELECT statement, but I would like to know whether I can do it with that.)
Not sure exactly what you try to denote with that syntax, but in almost all RDBMS-es you can use a subquery in the FROM clause (sometimes called an "inline-view"):
SELECT..
FROM (
SELECT ...
FROM ...
) my_select
WHERE ...
In advanced "enterprise" RDBMS-es (like oracle, SQL Server, postgresql) you can use common table expressions which allows you to refer to a query by name and reuse it even multiple times:
-- Define the CTE expression name and column list.
WITH Sales_CTE (SalesPersonID, SalesOrderID, SalesYear)
AS
-- Define the CTE query.
(
SELECT SalesPersonID, SalesOrderID, YEAR(OrderDate) AS SalesYear
FROM Sales.SalesOrderHeader
WHERE SalesPersonID IS NOT NULL
)
-- Define the outer query referencing the CTE name.
SELECT SalesPersonID, COUNT(SalesOrderID) AS TotalSales, SalesYear
FROM Sales_CTE
GROUP BY SalesYear, SalesPersonID
ORDER BY SalesPersonID, SalesYear;
(example from http://msdn.microsoft.com/en-us/library/ms190766(v=sql.105).aspx)
You can do this using the WITH clause of the SELECT statement:
;
WITH my_select As (SELECT ... FROM ...)
SELECT * FROM foo
WHERE id IN (SELECT MAX(id) FROM my_select GROUP BY name)
That's the ANSI/ISO SQL Syntax. I know that SQL Server, Oracle and DB2 support it. Not sure about the others...
Yes, but you can select only one column in your subselect
SELECT (SELECT id FROM bla) AS my_select FROM bla2
You could store this into a temporary table.
So instead of doing the CTE/sub query you would use a temp table.
Good article on these here http://codingsight.com/introduction-to-temporary-tables-in-sql-server/

How do write the following sql fragment for SQL Server 2000

Can Someone please help to how to write the below code in SQL Server2000? Thanks
WITH cte
AS (SELECT *,
( Row_number() OVER(ORDER BY productid, transactionid,
statusdate
DESC)
)AS
rownum
FROM #table),
cte2
AS (SELECT cte.*,
( CASE
WHEN cte.status = Isnull((SELECT t.status
FROM cte t
WHERE t.rownum = ( cte.rownum + 1
)),
'')
THEN 1
ELSE 0
END )AS rownum2
FROM cte)
SELECT cte2.productid,
cte2.transactionid,
cte2.details,
cte2.status,
cte2.statusdate,
cte2.requestdate
FROM cte2
WHERE rownum2 = 0
There is no OVER() function in 2000, so you will need to re-address/move the initial cte select. You already have a table variable #table, so I would modify this to include the row numbers, by adding a identity column.
You could then use a subquery to extract what you need from cte2.
DigbySwift's answer mentions that there is a problem with ROW_NUMBER and you need to solve that first. You can use the idea in his answer, and you could also look at answers to similar questions such as these:
ROW_NUMBER Alternative for SQL Server 2000
Row_Number simulation in Sql server 2000
However there's also another problem: SQL Server 2000 doesn't support CTEs (WITH ... AS ...).
To solve this use subselects instead of CTEs by removing the WITH clause and moving the definition of the CTE to the place(s) where it is used.
For example this query using CTEs:
WITH T1 AS (SELECT a,b,c FROM ...)
SELECT * FROM T1
Becomes:
SELECT *
FROM (
SELECT a,b,c FROM ...
) AS T1
Hopefully that is enough to get you started.

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

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.

Optimize sql query with the rank function

This query gets the top item in each group using the ranking function.
I want to reduce the number of inner selects down to two instead of three. I tried using the rank() function in the innermost query, but couldn't get it working along with an aggregate function. Then I couldn't use a where clause on 'itemrank' without wrapping it in yet another select statement.
Any ideas?
select *
from (
select
tmp.*,
rank() over (partition by tmp.slot order by slot, itemcount desc) as itemrank
from (
select
i.name,
i.icon,
ci.slot,
count(i.itemid) as itemcount
from items i
inner join citems ci on ci.itemid = i.itemid
group by i.name, i.icon, ci.slot
) as tmp
) as popularitems
where itemrank = 1
EDIT: using sql server 2008
In Oracle and Teradata (and perhaps others too), you can use QUALIFY itemrank = 1 to get rid of the outer select. This is not part of the ANSI standard.
You can use Common Table Expressions in Oracle or in SQL Server.
Here is the syntax:
WITH expression_name [ ( column_name [,...n] ) ]
AS
( CTE_query_definition )
The list of column names is optional only if distinct names for all resulting columns are supplied in the query definition.
The statement to run the CTE is:
SELECT <column_list>
FROM expression_name;