SQL Recursive CTE unexpectedly returns alternating sets - sql
I am trying to get the use recursive CTE to repeat the same pattern over and over, resetting when "Scenario" increases in value. RowNumber repeats 1-21 (as desired), but whenever "Scenario" is an even number, there are too few items in the "Vals" column to feed into "Value". I can't figure out which part of the code is causing me to be 1 short for only even Scenarios.
Below are the results of the code I'm using at the bottom.
Scenario RowNumber Value Vals
1 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 4 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 5 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 6 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 7 A A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 8 A A,A,A,A,A,A,A,A,A,A,A,B,C
1 9 A A,A,A,A,A,A,A,A,A,A,B,C
1 10 A A,A,A,A,A,A,A,A,A,B,C
1 11 A A,A,A,A,A,A,A,A,B,C
1 12 A A,A,A,A,A,A,A,B,C
1 13 A A,A,A,A,A,A,B,C
1 14 A A,A,A,A,A,B,C
1 15 A A,A,A,A,B,C
1 16 A A,A,A,B,C
1 17 A A,A,B,C
1 18 A A,B,C
1 19 A B,C
1 20 B C
1 21 C
2 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 4 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 5 A A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 6 A A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 7 A A,A,A,A,A,A,A,A,A,A,B,B,C
2 8 A A,A,A,A,A,A,A,A,A,B,B,C
2 9 A A,A,A,A,A,A,A,A,B,B,C
2 10 A A,A,A,A,A,A,A,B,B,C
2 11 A A,A,A,A,A,A,B,B,C
2 12 A A,A,A,A,A,B,B,C
2 13 A A,A,A,A,B,B,C
2 14 A A,A,A,B,B,C
2 15 A A,A,B,B,C
2 16 A A,B,B,C
2 17 A B,B,C
2 18 B B,C
2 19 B C
2 20 C
2 21 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
3 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 4 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 5 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 6 A A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 7 A A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 8 A A,A,A,A,A,A,A,A,A,A,B,C,C
3 9 A A,A,A,A,A,A,A,A,A,B,C,C
3 10 A A,A,A,A,A,A,A,A,B,C,C
3 11 A A,A,A,A,A,A,A,B,C,C
3 12 A A,A,A,A,A,A,B,C,C
3 13 A A,A,A,A,A,B,C,C
3 14 A A,A,A,A,B,C,C
3 15 A A,A,A,B,C,C
3 16 A A,A,B,C,C
3 17 A A,B,C,C
3 18 A B,C,C
3 19 B C,C
3 20 C C
3 21 C
4 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 4 A A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 5 A A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 6 A A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 7 A A,A,A,A,A,A,A,A,A,B,B,B,C
4 8 A A,A,A,A,A,A,A,A,B,B,B,C
4 9 A A,A,A,A,A,A,A,B,B,B,C
4 10 A A,A,A,A,A,A,B,B,B,C
4 11 A A,A,A,A,A,B,B,B,C
4 12 A A,A,A,A,B,B,B,C
4 13 A A,A,A,B,B,B,C
4 14 A A,A,B,B,B,C
4 15 A A,B,B,B,C
4 16 A B,B,B,C
4 17 B B,B,C
4 18 B B,C
4 19 B C
4 20 C
This is the code I used to generate the above sample. Where am I going wrong?
CREATE TABLE #temp3
(
Scenario INT
,Vals VARCHAR(64)
,LEN INT
)
;
WITH vals AS
(
SELECT
v.*
FROM
(VALUES ('A'), ('B'), ('C')) v(x)
),
CTE AS
(
SELECT CAST('A' AS VARCHAR(MAX)) AS STR, 0 AS LEN
UNION ALL
SELECT (CTE.STR + ',' + vals.x), CTE.LEN + 1
FROM
CTE
JOIN vals
ON vals.x >= RIGHT(CTE.STR, 1)
WHERE CTE.LEN < 19
)
INSERT INTO #temp3
SELECT
ROW_NUMBER() OVER(ORDER BY STR + ',C') AS Scenario
,STR + ',C' AS Vals
,LEN
FROM
CTE
WHERE
STR + 'C' LIKE '%B%'
AND LEN = 19
;
-- Split strings created above into individual characters
WITH cte(Scenario, Value, Vals) AS
(
SELECT
Scenario
,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10)) AS Value
,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '') AS Vals
FROM #temp3
UNION ALL
SELECT
Scenario
,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10))
,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '')
FROM cte
WHERE Vals > ''
)
SELECT
Scenario
,ROW_NUMBER() OVER (PARTITION BY Scenario ORDER BY Scenario) RowNumber
,Value
,Vals
FROM cte t
I'm not exactly sure what the problem you are describing is, but the ROW_NUMBER() should use an ORDER BY clause that completely orders the rows in each partition.
When you use "PARTITION BY Scenario ORDER BY Scenario" the order in which the ROW_NUMBER() values are assigned is undefined. Try something like
WITH cte(Scenario, depth, Value, Vals) AS
(
SELECT
Scenario, 0 depth
,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10)) AS Value
,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '') AS Vals
FROM #temp3
UNION ALL
SELECT
Scenario, depth+1
,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10))
,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '')
FROM cte
WHERE Vals > ''
)
SELECT
Scenario
,depth
,ROW_NUMBER() OVER (PARTITION BY Scenario ORDER BY depth ) RowNumber
,Value
,Vals
FROM cte t
Related
row_number() but only increment value after a specific value in a column
Query: SELECT (row_number() OVER ()) as grp, * from tbl Edit: the rows below are returned by a pgrouting shortest path function and it does have a sequence. seq grp id 1 1 8 2 2 3 3 3 2 4 4 null 5 5 324 6 6 82 7 7 89 8 8 null 9 9 1 10 10 2 11 11 90 12 12 null How do I make it so that the grp column is only incremented after a null value on id - and also keep the same order of rows seq grp id 1 1 8 2 1 3 3 1 2 4 1 null 5 2 324 6 2 82 7 2 89 8 2 null 9 3 1 10 3 2 11 3 90 12 3 null
demo:db<>fiddle Using a cumulative SUM aggregation is a possible approach: SELECT SUM( -- 2 CASE WHEN id IS NULL THEN 1 ELSE 0 END -- 1 ) OVER (ORDER BY seq) as grp, id FROM mytable If the current (ordered!) value is NULL, then make it 1, else 0. Now you got a bunch of zeros, delimited by a 1 at each NULL record. If you'd summerize these values cumulatively, at each NULL record, the sum increased. Execution of the cumulative SUM() using window functions This yields: 0 8 0 3 0 2 1 null 1 324 1 82 1 89 2 null 2 1 2 2 2 90 3 null As you can see, the groups start with the NULL records, but you are expecting to end it. This can be achieved by adding another window function: LAG(), which moves the records to the next row: SELECT SUM( CASE WHEN next_id IS NULL THEN 1 ELSE 0 END ) OVER (ORDER BY seq) as grp, id FROM ( SELECT LAG(id) OVER (ORDER BY seq) as next_id, seq, id FROM mytable ) s The result is your expected one: 1 8 1 3 1 2 1 null 2 324 2 82 2 89 2 null 3 1 3 2 3 90 3 null
SQL: subset data: select id when time_id for id satisfy a condition from another column
I have a data (dt) in SQL like the following: ID time_id act rd 11 1 1 1 11 2 4 1 11 3 7 0 12 1 8 1 12 2 2 0 12 3 4 1 12 4 3 1 12 5 4 1 13 1 4 1 13 2 1 0 15 1 3 1 16 1 8 0 16 2 8 0 16 3 8 0 16 4 8 0 16 5 8 0 and I want to take the subset of this data such that only ids (and their corresponding time_id, act, rd) that has time_id == 5 is retained. The desired output is the following ID time_id act rd 12 1 8 1 12 2 2 0 12 3 4 1 12 4 3 1 12 5 4 1 16 1 8 0 16 2 8 0 16 3 8 0 16 4 8 0 16 5 8 0 I know I should use having clause somehow but have not been successful so far (returns me empty outputs). below is my attempt: SELECT * FROM dt GROUP BY ID Having min(time_id) == 5;
This query: select id from tablename where time_id = 5 returns all the ids that you want in the results. Use it with the operator IN: select * from tablename where id in (select id from tablename where time_id = 5)
You can use a correlated subquery with exists: select t.* from t where exists (select 1 from t t2 where t2.id = t.id and t2.time_id = 5);
WITH temp AS ( SELECT id FROM tab WHERE time_id = 5 ) SELECT * FROM tab t join temp tp on(t.id=tp.id);
check this query select * from table t1 join (select distinct ID from table t where time_id = 5) t2 on t1.id =t2.id;
How to group/List all nodes of a undirected graph using teradata sql
I have data for many diff. set of undirected graphs in a table (like adjacent list relationship, one node is connected which all node) and I need to group all individual undirected graphs. Eg: all nodes of the particular undirected graphs will be in a group & group name will be the min. of the node. sel d.adj_node, min(d.adj_node) Over (Partition By a.node) as grp table a left join table b on a.adj_node=b.node left join table c on b.adj_node=c.node left join table d on c.adj_node=d.node; Now, I am doing a self-join for 4,5 times and then on top that query doing partitioning it to get the desired output. But doing self-join 4 5 times is creating performance issue. So, need some recursive sql, stored procedure or some other logic to do the same for all levels. Input Data & Required Output will be like this link Looking for some suggestions. Input Table node adj_node 1 2 2 1 2 3 2 5 2 6 2 7 3 2 3 4 4 3 4 5 4 6 4 7 5 2 5 4 6 2 6 4 6 8 7 2 7 4 8 6 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 10 11 11 10 11 13 11 14 12 13 12 14 13 11 13 12 13 14 14 11 14 12 14 13 10 10 11 11 12 12 13 13 14 14 Output node grp 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 10 10 11 10 12 10 13 10 14 10
I just remembered that I did something similar before using updates on a temp table. The best way to implement this would be a Stored Procedure with a loop in it: CREATE VOLATILE TABLE vt_tab AS ( SELECT DISTINCT NODE , adj_node, NODE AS grp FROM tab AS t1 WHERE adj_node <> NODE ) WITH DATA ON COMMIT PRESERVE ROWS ; -- REPEAT this update UNTIL activity_count = 0 UPDATE vt_tab FROM ( SELECT t2.NODE, MIN(t1.grp) AS mingrp FROM vt_tab AS t1 JOIN vt_tab AS t2 ON t1.adj_node = t2.NODE AND t1.grp < t2.grp GROUP BY t2.NODE ) x SET grp = mingrp WHERE vt_tab.NODE = x.NODE ; --get the final result SEL DISTINCT NODE,grp FROM vt_tab ORDER BY 1 ; Recursion might be possible, but there's a high probability that it will blow your spool because you need repeated m:n joins and only the final Select allows to reduce the result rows.
A solution with a recursive CTE: with cte as ( select node as node, node as grp from Tabl_1 Union all select C.node, T.adj_node from CTE C inner join Tabl_1 T on C.grp = T.node where T.adj_node < C.grp ) select node, MIN(grp) as grp from cte group by node order by node == EDIT 1 == Here is a new version to reflect your point. with cte as ( select node as node, node as grp, ',' + CAST(node as varchar(max)) + '-' + CAST(node as varchar(max)) + ',' as pair from Tabl_1 Union all select C.node, T.adj_node, C.pair + CAST(C.node as varchar(max)) + '-' + CAST(T.adj_node as varchar(max)) + ',' from CTE C inner join Tabl_1 T on C.grp = T.node where C.pair not like '%,' + CAST(C.node as varchar(max)) + '-' + CAST(T.adj_node as varchar(max)) + ',%' ) select node, MIN(grp) as grp from cte group by node order by node
Select Data based on Sum of another columns value
I have a Table with Data as RowIndex Id TicketCount 1 23 1 2 25 2 3 3 1 4 14 1 5 16 1 6 18 1 7 1 1 8 6 1 9 15 1 ===> at this row the sum of Ticket Count is 10 10 22 1 11 27 1 12 24 1 13 26 2 14 9 1 15 19 1 From this Data I want to Select All Records where The Sum of Ticket Count will be equal to 10(user input value) In the Given data I want to Select all Records till Row Index 9. Output should be: RowIndex Id TicketCount 1 23 1 2 25 2 3 3 1 4 14 1 5 16 1 6 18 1 7 1 1 8 6 1 9 15 1
SQL Server 2008 doesn't have the cumulative sum function. I implement it using a correlated subquery: select RowIndex, Id, TicketCount from (select t.*, (select sum(TicketCount) from t t2 where t2.RowIndex <= t.RowIndex ) as cumTicketCount from t ) t where cumTicketCount <= 10; In SQL Server 2012, you can phrase this using a window function: select RowIndex, Id, TicketCount from (select t.*, sum(TicketCount) over (order by RowIndex) as CumTicketCount from t ) t where cumTicketCount <= 10;
You can do it using recursive CTE: WITH RCTE AS ( SELECT *, TicketCount AS Total FROM Table1 WHERE RowIndex = 1 UNION ALL SELECT t.*, r.Total + t.TicketCount FROM RCTE r INNER JOIN Table1 t ON r.RowIndex + 1 = t.RowIndex WHERE r.Total + t.TicketCount <= 10 --your input value ) SELECT * FROM RCTE SQLFiddle DEMO
Convert row to column using sql server 2008?
Table name is Looupvalue id Ptypefield Value 1 1 D 2 1 E 3 1 F 4 1 G 5 1 H 6 2 FL 7 2 IF 8 2 VVS1 9 2 VVS2 10 2 VS1 11 2 VS2 12 3 0.50 13 3 1.00 14 3 1.50 15 3 2.00 16 4 Marquise 17 4 Round 18 4 Pear 19 4 Radiant 20 4 Princess Lookupvalue table value convert roow to column depends on ptypefield Like id 1 id 2 id 3 id 4 1 D 6 fl 12 0.50 16 Marquise 2 E 7 If 13 1 17 Round.... 3 F 8 vvs2 14 1.5 4 G 9 vvs2 15 2 5 H 10 vs1 11 vs2 Thanks
In your sample output, it is not clear why values from columns 1 and 2 would be related to columns 3 and 4. However, here is a possible solution: ;With RowNumbers As ( Select Id, PTypeField, Value , Row_Number() Over( Partition By PTypeField Order By Id ) As Rownum From #Test ) Select RowNum , Min( Case When PTypeField = 1 Then Id End ) As Id , Min( Case When PTypeField = 1 Then Value End ) As [1] , Min( Case When PTypeField = 2 Then Id End ) As Id , Min( Case When PTypeField = 2 Then Value End ) As [2] , Min( Case When PTypeField = 3 Then Id End ) As Id , Min( Case When PTypeField = 3 Then Value End ) As [3] , Min( Case When PTypeField = 4 Then Id End ) As Id , Min( Case When PTypeField = 4 Then Value End ) As [4] From RowNumbers Group By RowNum If you wanted to dynamically generate the columns, the only way to do that in SQL is to use some fugly dynamic SQL. T-SQL was not designed for this sort of output and instead you should use a reporting tool or do the crosstabbing in a middle tier component or class. This data schema looks like an EAV which would explain why retrieving the data you want is so difficult.