I am creating a CTE as I need all InquiryId in a variable in order to proceed to a WHILE loop.
I am trying to fill a variable via a CTE like below
DECLARE #inqIdsToClose TABLE (InquiryId int)
With CTE (InquiryId,InquirySubject,CreateDt,SendTo,[From],SendBy) as
(
Select I.InquiryId,I.InquirySubject,I.CreateDt,I.SendTo,I.[From],U.Email as SendBy
From Inquiries I
Inner Join Users U
ON I.[From] = U.UserID
Where I.InquiryId Not In (Select InquiryId from InquiryReply)
And I.InquiryStatusId <> 5
And DATEDIFF(day, I.CreateDt, getdate()) >=7
)
Insert into #inqIdsToClose
Select InquiryId from CTE
Print #inqIdsToClose;
But SQL Server is not allowing me to fill #inqIdsToClose
The InquiryId which are returned by CTE are multiple which I will later use in a while loop.
You were missing the terminator after you declared your table variable
Example
DECLARE #inqIdsToClose TABLE (InquiryId int);
with cte as (
-- Dummy query
Select SomeVal=1
Union All
Select SomeVal=2
)
Insert Into #inqIdsToClose Select SomeVal from cte
Select * from #inqIdsToClose
Returns
InquiryId
1
2
Related
I have a sql server table showing the IDs and their previous IDs,
create table test2 ( ID varchar(10) ,
Pre_ID varchar(10)
)
insert into test2 values ('e','d')
, ('d','c')
, ('c','b')
, ('b','a')
, ('a',null)
, ('r','q')
, ('q','p')
, ('p',null)
the table is like this:
The result should be like this:
I have successfully got the levels using a recursive cte, but could not get the correct group for them. can anyone help? Thanks.
This is my code:
with cte as (
select id, Pre_ID, level
from #temp2
where Pre_ID is null
union all
select t2.id, t2.Pre_ID, t2.level
from cte
inner join #temp2 t2
on t2.Pre_ID=cte.id
)
select * from cte
order by id
What you need to do is start with the first level and add a ROW_NUMBER to that, then join all further levels recursively:
with cte as (
select id, Pre_ID, level, row_number() over (order by ID) as grp
from #temp2
where Pre_ID is null
union all
select t2.id, t2.Pre_ID, t2.level, cte.grp
from cte
inner join #temp2 t2
on t2.Pre_ID=cte.id
)
select * from cte
order by id;
Suppose you have a table with non-unique values such as this:
CREATE TABLE accounts ( fname VARCHAR(20), lname VARCHAR(20))
GO
INSERT accounts VALUES ('Fred', 'Flintstone')
INSERT accounts VALUES ('Fred', 'Flintstone')
INSERT accounts VALUES ('Fred', 'Flintstone')
SELECT * FROM accounts
GO
Now using a ROW_NUMBER function, you can get a unique incrementing row number.
select *, ROW_NUMBER() over(order by (select null)) as rn
from accounts
But how do we this without using a ROW_NUMBER function. I tried giving each row a unique ID using NEWID() and then counting the rows as given below but it did not work as it gives me a non-unique number which does not start with 1.
Note that I do not want to alter the table to add a new column.
;with cte as
(select *
from accounts as e
cross apply (select newid()) as a(id)
)
select *, (select count(*)+1 from cte as c1 where c.id > c1.id) as rn
from cte as c
order by rn
SQL Fiddle for toying around is http://sqlfiddle.com/#!18/c270f/3/0
The following demonstrates why your code fails, but does not provide an alternative to Row_Number().
A column, TopId, is added to the final select that should get the minimum value generated by NewId() and report it in every row. Instead, a new value is generated for each row.
-- Sample data.
declare #Samples as Table ( FName VarChar(20), LName VarChar(20) );
insert into #Samples ( FName, LName ) values
( 'Fred', 'Flintstone' ), ( 'Fred', 'Flintstone' ), ( 'Fred', 'Flintstone' );
select * from #Samples;
-- Cross apply NewId() in a CTE.
;with cte as
( select *
from #Samples as S
cross apply ( select NewId() ) as Ph( Id ) )
select *, ( select count(*) from cte as c1 where c1.Id >= c.Id ) as RN,
-- The following column should output the minimum Id value from the table for every row.
-- Instead, it generates a new unique identifier for each row.
( select top 1 id from cte order by id ) as TopId
from cte as c
order by RN;
The execution plan shows that the CTE is treated as a view that is being evaluated repeatedly, thus generating conflicting Id values.
How about this:
SELECT
src.*,
SUM(DummyVal) OVER(ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS RowId
FROM (
SELECT a.*, 1 AS DummyVal
FROM MyTable a
) src
It's still a window function, though, not sure if that matters.
Fiddle me this
You can create an function yourself to compute the row_number,
In this example, I had to calculate an index for a lesson within a course.
Window Function version:
SELECT *, ROW_NUMBER() OVER(PARTITION BY courseId) AS row_num FROM lessons;
I created helper-function, to compute the row_number without window function:
DELIMITER $$
CREATE FUNCTION getRowNumber (lessonId int, courseId int)
RETURNS int
DETERMINISTIC
BEGIN
DECLARE count int;
select count(l2.id) into count from lessons l2 where l2.courseId=courseId
and l2.id<=lessonId;
RETURN count;
END$$
DELIMITER ;
so, the final query is:
SELECT l.*, getRowNumber(l.id,l.courseId) as row_num FROM lessons l;
got the same result as the first query!
MySQL:
SELECT #rownum := #rownum + 1 AS rank, a.*
FROM accounts a,(SELECT #rownum := 0) r;
In ORACLE it would simply be
SELECT ROWNUM, a.*
FROM accounts a;
Both without window
I have table described bellow from which I need to select all rows with [Value] greater for example at least 5 points than [Value] from previous row (ordered by [Id]). Starting with first row of [Id] 1, desired output would be:
[Id] [Value]
---------------
1 1
4 12
8 21
Code:
declare #Data table
(
[Id] int not null identity(1, 1) primary key,
[Value] int not null
);
insert into #Data ([Value])
select 1 [Value]
union all
select 5
union all
select 3
union all
select 12
union all
select 8
union all
select 9
union all
select 16
union all
select 21;
select [t1].*
from #Data [t1];
Edit:
So, based on JNevill's and Hogan's answers I end with this:
;with [cte1]
as (
select [t1].[Id],
[t1].[Value],
cast(1 as int) [rank]
from #Data [t1]
where [t1].[Id] = 1
union all
select [t2].[Id],
[t2].[Value],
cast(row_number() over (order by [t2].id) as int) [rank]
FROM [cte1] [t1]
inner join #Data [t2] on [t2].[value] - [t1].[value] > 5
and [t2].[Id] > [t1].[Id]
where [t1].[rank] = 1
)
select [t1].[Id],
[t1].[Value]
from [cte1] [t1]
where [t1].[rank] = 1;
which is working. Alan Burstein answer is correct too (but applicable only on MSSQL 2012+ - due to LAG fc). I will do some performance tests (I'm on 2016 version) and will see performance over my real data (approx. 30 millions of records).
If you are on 2012+ you can use LAG which will provide a better performing solution that a recursive CTE. I'm including your sample data so you can just copy/paste/test...
-- Your sample data
DECLARE #Data TABLE
(
Id int not null identity(1, 1) primary key,
Value int not null
);
insert into #Data ([Value])
select 1 [Value] union all select 5 union all select 3 union all select 12 union all
select 8 union all select 9 union all select 16 union all select 21;
-- Solution using window functions
WITH
prevRows AS
(
SELECT t1.Id, t1.Value, prevDiff = LAG(t1.Value, 1) OVER (ORDER BY t1.id) - t1.Value
FROM #Data t1
),
NewPrev AS
(
SELECT t1.Id, t1.Value, NewDiff = Value - LAG(t1.Value,1) OVER (ORDER BY t1.id)
FROM prevRows t1
WHERE prevDiff <= -5 OR prevDiff IS NULL
)
SELECT t1.Id, t1.Value
FROM NewPrev t1
WHERE NewDiff >= 5 OR NewDiff IS NULL;
I believe the best way to pull this off is using a recursive CTE. A Recursive CTE is a special type of CTE that refers back to itself. It's made up of two parts.
The recursive seed/anchor which establishes the beginning of the recursion. In your case, record with ID=1.
The recursive term/member which is the statement that refers back to itself by the name of the CTE. Here we pull through the next record that is greater than 5 from the previous found record according to the ID sorted ascending.
Code:
WITH RECURSIVE recCTE AS
(
/*Select first record for recursive seed/anchor*/
SELECT
id,
value,
cast(1 as INT) as [rank]
FROM table
WHERE id = 1
UNION ALL
/*find the next value that is more than 5 from the current value*/
SELECT
table.id,
table.value
ROW_NUMBER() OVER (ORDER BY id)
FROM
recCTE INNER JOIN table
ON table.value - recCTE.value > 5
AND table.id > recCTE.id
WHERE recCTE.[rank]=1
)
SELECT id, value FROM recCTE;
I've made use of the Row_Number() Window Function to find the rank of the matching record by ID sorted Ascending. With the WHERE clause in the recursive term we only grab the first found record that is 5 more than the previous found record. Then we head into the next recursive step.
You can do it with a recursive CTE
with find_values as
(
-- Find first value
SELECT Value
FROM #Table
ORDER BY ID ASC
FETCH FIRST 1 ROW ONLY
UNION ALL
-- Find next value
SELECT Value
FROM #Table
CROSS JOIN find_values
WHERE Value >= find_values.Value + 5
ORDER BY ID ASC
FETCH FIRST 1 ROW ONLY
)
SELECT *
FROM find_values
I need to be able to "look back" during the execution of a CTE. Using the sample data below I need an additional column returned which represents the balance as of the BalanceDate column. So rowid 6 would return 30 because that is the balance as of 3/6/2015. Rowid 9 would return 80 because that is the last record closest to 9/30/2015. When I try to use the CTE as a derived table I receive the error
Msg 253, Level 16, State 1, Procedure
Recursive member of a common table expression 'CTE' has multiple recursive references.
--Sample data
CREATE TABLE #TEMP
(RowID int null,
TranDate date null,
Amount int null,
BalanceDate date null);
INSERT INTO #TEMP (RowID,TranDate, Amount, BalanceDate)
SELECT 1,'1/15/2015',10,null
UNION ALL
SELECT 2,'2/18/2015',10,null
UNION ALL
SELECT 3,'3/6/2015',10,null
UNION ALL
SELECT 4,'6/1/2015',10,null
UNION ALL
SELECT 5,'6/18/2015',10,null
UNION ALL
SELECT 6,'7/31/2015',10,'3/6/2015'
UNION ALL
SELECT 7,'8/2/2015',10,null
UNION ALL
SELECT 8,'9/13/2015',10,null
UNION ALL
SELECT 9,'11/15/2015',10,'9/30/2015';
with CTE
as
( SELECT RowID, TranDate, Amount, Balance=Amount, BalanceDate FROM #TEMP WHERE RowID = 1
UNION ALL
SELECT #TEMP.RowID,#TEMP.TranDate, #TEMP.Amount, Balance = #TEMP.Amount + CTE.Balance, #TEMP.BalanceDate
FROM #TEMP
INNER JOIN CTE on #Temp.RowID = CTE.RowID + 1
)
SELECT * FROM CTE;
I generally avoid recursive CTEs unless there's a reason you need to use them (for example, to find parents and children). Rather than using a recursive CTE, you can do something like this to get what you want:
EDIT:
If there are ever negative amounts, this would produce the correct result.
SELECT RowID, T1.TranDate, Amount
, (SELECT SUM(Amount) FROM #TEMP WHERE TranDate <= T1.TranDate) Balance
, BalanceDate
, (SELECT SUM(Amount) FROM #TEMP WHERE TranDate <= T1.BalanceDate) BalanceToDate
FROM #TEMP T1
Edited out other queries that were here before to make this less messy.
Using original recursive CTE:
; WITH CTE AS
(
SELECT RowID, TranDate, Amount, Balance=Amount, BalanceDate
FROM TEMP
WHERE RowID = 1
UNION ALL
SELECT TEMP.RowID,TEMP.TranDate, TEMP.Amount, Balance = TEMP.Amount + CTE.Balance, TEMP.BalanceDate
FROM TEMP
JOIN CTE on Temp.RowID = CTE.RowID + 1
)
SELECT C.RowID, C.TranDate, C.Amount, C.Balance, C.BalanceDate
, (SELECT SUM(Amount) FROM CTE WHERE TranDate <= C.BalanceDate) BalanceToDate
FROM CTE C
I want to retrieve the parentid of an id, if that parentid has a parent again retrieve it, and so on.
Kind of hierarchy table.
id----parentid
1-----1
5-----1
47894--5
47897--47894
am new to sql server and tried, some queries like:
with name_tree as
(
select id, parentid
from Users
where id = 47897 -- this is the starting point you want in your recursion
union all
select c.id, c.parentid
from users c
join name_tree p on p.id = c.parentid -- this is the recursion
)
select *
from name_tree;
It is giving me only one row.
and also I want to insert these records into a temporary table variable.
How can I do this. thanks in advance. sorry for asking the simple question(though not to me)
Try this to get all parents of a child
;with name_tree as
(
select id, parentid
from Users
where id = 47897 -- this is the starting point you want in your recursion
union all
select C.id, C.parentid
from Users c
join name_tree p on C.id = P.parentid -- this is the recursion
-- Since your parent id is not NULL the recursion will happen continously.
-- For that we apply the condition C.id<>C.parentid
AND C.id<>C.parentid
)
-- Here you can insert directly to a temp table without CREATE TABLE synthax
select *
INTO #TEMP
from name_tree
OPTION (MAXRECURSION 0)
SELECT * FROM #TEMP
Click here to view result
EDIT :
If you want to insert into a table variable, you can do something like:
-- Declare table varialbe
Declare #TABLEVAR table (id int ,parentid int)
;with name_tree as
(
select id, parentid
from #Users
where id = 47897 -- this is the starting point you want in your recursion
union all
select C.id, C.parentid
from #Users c
join name_tree p on C.id = P.parentid -- this is the recursion
-- Since your parent id is not NULL the recursion will happen continously.
-- For that we apply the condition C.id<>C.parentid
AND C.id<>C.parentid
)
-- Here you can insert directly to table variable
INSERT INTO #TABLEVAR
select *
from name_tree
OPTION (MAXRECURSION 0)
SELECT * FROM #TABLEVAR
Click here to view result
Your query is doing recursion but in opposite direction. So if you change starting point to:
where id = 1
then you will have user 1 and all his successors
you didn't mention the desired output and input.
However you can try like this,
Declare #t table (id int ,parentid int)
insert into #t
select 1,1 union all
select 5,1 union all
select 47894,5 union all
select 47897,47894
;With CTE as
(
select * from #t where id=1
union all
Select a.* from #t a inner join cte b
on b.id=a.parentid and
a.id<>b.id
)
select * from cte