SQL query - get parent index from level and child index - tsql
I have dataset with two columns: index and level.
Level is number indicating level in hierarchy of nested parent child records.
The records are in order of hierarchy and index is just the line number of record.
The rule is that any record's parent record has level = child level - 1.
My challenge is to identify the parent's index based on this rule.
For each record, I need to SQL query that will get the record's parent index.
The SQL query will be a self join, and get the max index value where the self join index < child.index and the self join level = child.level
I need help to figure out how to write this SQL.
I can use MS Access or use SQL in VBA to perform this query.
This is a visual representation of the data set.
This is sample data and expected result .. want to get parent index .. parent level is child level - 1.
Index,Level Number,Parent Level,Parent Index
1,1,1,1
2,2,1,1
4,4,3,3
9,9,8,8
3,3,2,2
5,5,4,4
8,8,7,7
6,6,5,5
7,7,6,6
10,10,9,9
11,11,10,10
12,12,11,11
13,13,12,12
14,14,13,13
15,14,13,13
16,14,13,13
17,14,13,13
18,14,13,13
19,14,13,13
20,14,13,13
21,13,12,12
22,13,12,12
23,13,12,12
24,14,13,23
25,14,13,23
26,14,13,23
27,11,10,10
28,9,8,8
29,9,8,8
30,9,8,8
31,9,8,8
32,9,8,8
33,9,8,8
34,9,8,8
35,8,7,7
36,9,8,35
37,10,9,36
38,11,10,37
39,11,10,37
40,12,11,39
41,12,11,39
42,13,12,41
43,13,12,41
44,13,12,41
45,11,10,37
46,12,11,45
47,13,12,46
48,14,13,47
49,14,13,47
50,14,13,47
51,14,13,47
52,14,13,47
53,14,13,47
54,14,13,47
55,13,12,46
56,13,12,46
57,13,12,46
58,9,8,35
59,9,8,35
60,9,8,35
61,9,8,35
62,8,7,7
63,8,7,7
64,8,7,7
65,8,7,7
66,8,7,7
67,8,7,7
68,8,7,7
Edited to add:
I tried to do this in Excel Power Query, and found an answer, but it takes forever to run so need to find SQL VBA/ADO solution. But here is Power Query solution to help give ideas about how to do it SQL.
let
Source = Excel.CurrentWorkbook(){[Name="Tabelle3"]}[Content],
ParentIndex = Table.AddColumn(Source, "ParentIndex", each let Index=[Index], LN=[Level Number] in List.Max(Table.SelectRows(Source, each _[Index] < Index and _[Level Number]=LN-1)[Index])),
#"Merged Queries" = Table.NestedJoin(ParentIndex,{"ParentIndex"},ParentIndex,{"Index"},"NewColumn",JoinKind.LeftOuter),
#"Expanded NewColumn" = Table.ExpandTableColumn(#"Merged Queries", "NewColumn", {"Level Number"}, {"Level Number.1"})
in
#"Expanded NewColumn"
This Power Query solution finds Max index where each row index < all index and level = level -1
DECLARE #t TABLE (val INT)
INSERT INTO #t
VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),
(14),(14),(14),(14),(14),(14),(14),(13),(13),(13),(14),(14),(14),(11)
SELECT REPLICATE(' ', val) + CAST(val AS VARCHAR(10))
FROM #t
Output
-----------------------------
1
2
3
4
5
6
7
8
9
10
11
12
13
14
14
14
14
14
14
14
13
13
--http://stackoverflow.com/questions/36639349/sql-query-get-parent-index-from-level-and-child-index
declare #table table
(idx int, level int)
insert into #table
(idx,level)
values
(1,1),
(2,2),
(3,3),
(4,4),
(5,5),
(6,6),
(7,7),
(8,8),
(9,9),
(10,10),
(11,11),
(12,12),
(13,13),
(14,14),
(15,14),
(16,14),
(17,14),
(18,14),
(19,14),
(20,14),
(21,14),
(22,13),
(23,13),
(24,13),
(25,14),
(26,14),
(27,14),
(28,11),
(29,9),
(30,8)
select v.idx,v.level,v.parentlevel,u.idx parentidx
from
(
select s.* from --Find the first idx,level
(
select t.*, t.level - 1 as parentlevel,
row_number() over (partition by level order by idx,level) rownum
from #table t
) s
where rownum = 1
) u
join --join to every occurance of
(select t2.*, t2.level - 1 parentlevel,
1 as rownum
from #table t2
) v
on (v.parentlevel = u.level and v.rownum = u.rownum)
union --and put 1 back
select w.idx,w.level,w.level,w.idx
from #table w
where w.idx = 1
order by v.idx
Related
After doing CTE Select Order By and then Update, Update results are not ordered the same (TSQL)
The code is roughly like this: WITH cte AS ( SELECT TOP 4 id, due_date, check FROM table_a a INNER JOIN table_b b ON a.linkid = b.linkid WHERE b.status = 1 AND due_date > GetDate() ORDER BY due_date, id ) UPDATE cte SET check = 1 OUTPUT INSERTED.id, INSERTED.due_date Note: the actual data has same due_date. When I ran the SELECT statement only inside the cte, I could get the result, for ex: 1, 2, 3, 4. But after the UPDATE statement, the updated results are: 4, 1, 2, 3 Why is this (order-change) happening? How to keep or re-order the results back to 1,2,3,4 in this same 1 query?
In MSDN https://msdn.microsoft.com/pl-pl/library/ms177564(v=sql.110).aspx you can read that There is no guarantee that the order in which the changes are applied to the table and the order in which the rows are inserted into the output table or table variable will correspond. Thats mean you can't solve your problem with only one query. But you still can use one batch to do what you need. Because your output don't guarantee the order then you have to save it in another table and order it after update. This code will return your output values in order that you assume: declare #outputTable table( id int, due_date date); with cte as ( select top 4 id, due_date, check from table_a a inner join table_b b on a.linkid = b.linkid where b.status = 1 and due_date > GetDate() order by due_date, id ) update cte set check = 1 output inserted.id, inserted.due_date into #outputTable; select * from #outputTable order by due_date, id;
PostgreSQL: set a column with the ordinal of the row sorted via another field
I have a table segnature describing an item with a varchar field deno and a numeric field ord. A foreign key fk_collection tells which collection the row is part of. I want to update field ord so that it contains the ordinal of that row per each collection, sorted by field deno. E.g. if I have something like [deno] ord [fk_collection] abc 10 aab 10 bcd 10 zxc 20 vbn 20 Then I want a result like [deno] ord [fk_collection] abc 1 10 aab 0 10 bcd 2 10 zxc 1 20 vbn 0 20 I tried with something like update segnature s1 set ord = (select count(*) from segnature s2 where s1.fk_collection=s2.fk_collection and s2.deno<s1.deno ) but query is really slow: 150 collections per about 30000 items are updated in 10 minutes about. Any suggestion to speed up the process? Thank you!
You can use a window function to generate the "ordinal" number: with numbered as ( select deno, fk_collection, row_number() over (partition by fk_collection order by deno) as rn, ctid as id from segnature ) update segnature set ord = n.rn from numbered n where n.id = segnature.ctid; This uses the internal column ctid to uniquely identify each rows. The ctid comparison is quite slow, so if you have a real primary (or unique) key in that table, use that column instead. Alternatively without the common table expression: update segnature set ord = n.rn from ( select deno, fk_collection, row_number() over (partition by fk_collection order by deno) as rn, ctid as id from segnature ) as n where n.id = segnature.ctid; SQLFiddle example: http://sqlfiddle.com/#!15/e997f/1
how to do dead reckoning on column of table, postgresql
I have a table looks like, x y 1 2 2 null 3 null 1 null 11 null I want to fill the null value by conducting a rolling function to apply y_{i+1}=y_{i}+x_{i+1} with sql as simple as possible (inplace) so the expected result x y 1 2 2 4 3 7 1 8 11 19 implement in postgresql. I may encapsulate it in a window function, but the implementation of custom function seems always complex
WITH RECURSIVE t AS ( select x, y, 1 as rank from my_table where y is not null UNION ALL SELECT A.x, A.x+ t.y y , t.rank + 1 rank FROM t inner join (select row_number() over () rank, x, y from my_table ) A on t.rank+1 = A.rank ) SELECT x,y FROM t;
You can iterate over rows using a recursive CTE. But in order to do so, you need a way to jump from row to row. Here's an example using an ID column: ; with recursive cte as ( select id , y from Table1 where id = 1 union all select cur.id , prev.y + cur.x from Table1 cur join cte prev on cur.id = prev.id + 1 ) select * from cte ; You can see the query at SQL Fiddle. If you don't have an ID column, but you do have another way to order the rows, you can use row_number() to get an ID: ; with recursive sorted as ( -- Specify your ordering here. This example sorts by the dt column. select row_number() over (order by dt) as id , * from Table1 ) , cte as ( select id , y from sorted where id = 1 union all select cur.id , prev.y + cur.x from sorted cur join cte prev on cur.id = prev.id + 1 ) select * from cte ; Here's the SQL Fiddle link.
Summing From Consecutive Rows
Assume we have a table and we want to do a sum of the Expend column so that the summation only adds up values of the same Week_Name. SN Week_Name Exp Sum -- --------- --- --- 1 Week 1 10 0 2 Week 1 20 0 3 Week 1 30 60 4 Week 2 40 0 5 Week 2 50 90 6 Week 3 10 0 I will assume we will need to `Order By' Week_Name, then compare the previous Week_Name(previous row) with the current row Week_name(Current row). If both are the same, put zero in the SUM column. If not the same, add all expenditure, where Week_Name = Week_Name(Previous row) and place in the Sum column. The final output should look like the table above. Any help on how to achieve this in T-SQL is highly appreciated.
Okay, I was eventually able to resolve this issue, praise Jesus! If you want the exact table I gave above, you can use GilM's response below, it is perfect. If you want your table to have running Cumulatives, i.e. Rows 3 shoud have 60, Row 5, should have 150, Row 6 160 etc. Then, you can use my code below: USE CAPdb IF OBJECT_ID ('dbo.[tablebp]') IS NOT NULL DROP TABLE [tablebp] GO CREATE TABLE [tablebp] ( tablebpcCol1 int PRIMARY KEY ,tabledatekey datetime ,tableweekname varchar(50) ,expenditure1 numeric ,expenditure_Cummulative numeric ) INSERT INTO [tablebp](tablebpcCol1,tabledatekey,tableweekname,expenditure1,expenditure_Cummulative) SELECT b.s_tablekey,d.PK_Date,d.Week_Name, SUM(b.s_expenditure1) AS s_expenditure1, SUM(b.s_expenditure1) + COALESCE((SELECT SUM(s_expenditure1) FROM source_table bs JOIN dbo.Time dd ON bs.[DATE Key] = dd.[PK_Date] WHERE dd.PK_Date < d.PK_Date),0) FROM source_table b INNER JOIN dbo.Time d ON b.[Date key] = d.PK_Date GROUP BY d.[PK_Date],d.Week_Name,b.s_tablekey,b.s_expenditure1 ORDER BY d.[PK_Date] ;WITH CTE AS ( SELECT tableweekname ,Max(expenditure_Cummulative) AS Week_expenditure_Cummulative ,MAX(tablebpcCol1) AS MaxSN FROM [tablebp] GROUP BY tableweekname ) SELECT [tablebp].* ,CASE WHEN [tablebp].tablebpcCol1 = CTE.MaxSN THEN Week_expenditure_Cummulative ELSE 0 END AS [RunWeeklySum] FROM [tablebp] JOIN CTE on CTE.tableweekname = [tablebp].tableweekname
I'm not sure why your SN=6 line is 0 rather than 10. Do you really not want the sum for the last Week? If having the last week total is okay, then you might want something like: ;WITH CTE AS ( SELECT Week_Name,SUM([Expend.]) as SumExpend ,MAX(SN) AS MaxSN FROM T GROUP BY Week_Name ) SELECT T.*,CASE WHEN T.SN = CTE.MaxSN THEN SumExpend ELSE 0 END AS [Sum] FROM T JOIN CTE on CTE.Week_Name = T.Week_Name Based on the requst in the comment wanting a running total in SUM you could try this: ;WITH CTE AS ( SELECT Week_Name, MAX(SN) AS MaxSN FROM T GROUP BY Week_Name ) SELECT T.SN, T.Week_Name,T.Exp, CASE WHEN T.SN = CTE.MaxSN THEN (SELECT SUM(EXP) FROM T T2 WHERE T2.SN <= T.SN) ELSE 0 END AS [SUM] FROM T JOIN CTE ON CTE.Week_Name = T.Week_Name ORDER BY SN
PostgreSQL: getting ordinal rank (row index? ) efficiently
You have a table like so: id dollars dollars_rank points points_rank 1 20 1 35 1 2 18 2 30 3 3 10 3 33 2 I want a query that updates the table's rank columns (dollars_rank and points_rank) to set the rank for the given ID, which is just the row's index for that ID sorted by the relevant column in a descending order. How best to do this in PostgreSQL?
The window function dense_rank() is what you need - or maybe rank(). The UPDATE could look like this: UPDATE tbl SET dollars_rank = r.d_rnk , points_rank = r.p_rnk FROM ( SELECT id , dense_rank() OVER (ORDER BY dollars DESC NULLS LAST) AS d_rnk , dense_rank() OVER (ORDER BY points DESC NULLS LAST) AS p_rnk FROM tbl ) r WHERE tbl.id = r.id; fiddle NULLS LAST is only relevant if the involved columns can be NULL: Sort by column ASC, but NULL values first?