Get the immediate parent child relation in a table - sql

I have a table with following value
userid roleid ranklvl
123 a 1
456 b 2
789 c 3
I need the output data from the above in the following format :
userid roleid ranklvl parentroleid
123 a 1 null
456 b 2 a
789 c 3 b
Thanks in advance for any guidance on this.
EDIT :
I used the while loop approach to achieve this but trying to avoid the while loop.
declare #a table
(
userid int,roleid char(1),ranklvl int,parentroleid char(1) )
insert into #a(userid,roleid,ranklvl) select userid,roleid,ranklvl from Table [where some condition]
declare #maxcount smallint = (select max(ranklvl) from #a)
declare #counter smallint = 1
declare #parentroleid char(1)
while( #maxcount > #counter)
BEGIN
Select #parentroleid = roleid from #a where ranklvl = #maxcount - 1
UPDATE #a SET parentrole = #parentroleid where ranklvl = #maxcount
SET #maxcount = #maxcount -1
END
select * from #a
The while loop logic worked if proper sequence of ranklvl is there for a given record set like 1->2->3. But it did not work if data is in following way :
userid roleid ranklvl
123 a 1
789 c 3
The following expected result is not coming by the while loop logic.
userid roleid ranklvl parentroleid
123 a 1 null
789 c 3 a

I think you want a self-join:
select t.*, p.roleid parentroleid
from mytable t
left join mytable p on p.ranklvl = t.ranklvl - 1
If there are gaps in the ordering column, you can enumerate first:
with cte as (
select t.*, row_number() over(order by ranklvl) rn
from mytable t
)
select c.*, p.roleid parentroleid
from cte c
left join cte p on p.rn = c.rn - 1

Related

Change the sequence using SQL Query

Want to sequence with SQL query.
Is there any shorter way to update it, or should I update the whole table.
My table as follows:
DECLARE #tab TABLE (ID INT IDENTITY, Name VARCHAR(10), Seq INT)
INSERT INTO #tab VALUES('A',1),('B',1),('C',1),('D',3),('E',4),('F',5),('G',6),('H',7),('I',8)
SELECT * FROM #tab ORDER BY Seq
I want to change the sequence show column with ID 7, 8, 9 at the top.
My desired output should be
DECLARE #tab TABLE (ID INT IDENTITY, Name VARCHAR(10), Seq INT)
INSERT INTO #tab VALUES('A',4),('B',5),('C',6),('D',7),('E',8),('F',9),('G',1),('H',2),('I',3)
SELECT * FROM #tab ORDER BY Seq
ROW_NUMBER is your friend here, just have to compute 2 different ones depending on the name breaking point.
;WITH RowNumbers AS
(
SELECT
T.ID,
RegularRowNumber = 3 + ROW_NUMBER() OVER (ORDER BY T.Name),
AfterGRowNumber = -6 + ROW_NUMBER() OVER (ORDER BY T.Name),
T.Seq,
T.Name
FROM
#tab AS T
)
UPDATE R SET
Seq = CASE WHEN R.Name >= 'G' THEN AfterGRowNumber ELSE RegularRowNumber END
FROM
RowNumbers AS R
Result:
ID Name Seq
1 A 4
2 B 5
3 C 6
4 D 7
5 E 8
6 F 9
7 G 1
8 H 2
9 I 3
Might want to consider using ID to order instead of Name, if appropriate.

how to sql recursion solve first node first then move to another in CTE

suppose i have a data like that
ID ParentID Name
1 null a
2 1 b
3 2 c
4 1 d
5 4 e
if i use cte(common table expression) provided by sql it shows me result like this
ID ParentID Name
1 null a
2 1 b
4 1 d
3 2 c
5 4 e
but i want to arrange data like, query should complete first node till end , then move to other node . like
ID ParentID Name
1 null a
2 1 b
3 2 c
4 1 d
5 4 e
Note: i have a primary key with datatype :uniqueidentifier so i cannot use order by clause after CTE
Example
Declare #Top int = null --<< Sets top of Hier Try 2
;with cteP as (
Select ID
,ParentID
,Name
,Path = cast('/'+[ID]+'/' as varchar(500))
From YourTable
Where IsNull(#Top,-1) = case when #Top is null then isnull(ParentID ,-1) else ID end
Union All
Select r.ID
,r.ParentID
,r.Name
,cast(p.path + '/'+r.[ID]+'/' as varchar(500))
From YourTable r
Join cteP p on r.ParentID = p.ID)
Select ID
,ParentID
,Name
From cteP A
Order By Path
Returns
Without seeing your query, I think you can do something like this:
WITH CTE_Example
AS
(
YOUR QUERY
)
SELECT *
FROM CTE_Example
ORDER BY ID

How to increment value when they are same

I have a data
By using this query I'm getting data like this
Select ID,Val,Premium,Row_number()OVER(PARTITION BY ID,Val ORDER BY ID) RN From Table1
Present Data
ID Val Premium RN
1 CH 201 1
1 CH 0 1
1 CHH 301 2
1 CHS 401 3
How can I make this data to like on the present Query
ID Val Premium RN
1 CH 201 1
1 CH 0 4
1 CHH 301 2
1 CHS 401 3
I just want to make the data which is same RN to increment to maximum number
Val = CH have RN = 1 then I want to make Premium = 0 record to RN = 4
In the present Row number I need to pick MAX(RN) and increment to that duplicate value
This should do the trick:
UPDATE table1
SET rn = (SELECT Max(rn) + 1
FROM table1)
WHERE id IN (SELECT id
FROM table1
WHERE rn IN (SELECT rn
FROM table1
GROUP BY rn
HAVING Count(*) > 1))
AND premium = 0;
Update the table. Setting the new RN value to the highest value+1. Only updating the rows where there are multiple RN values and where premium is 0.
--Try This
BEGIN TRAN
Declare #Strt INT,#End INT,#MaxNo INT
CREATE TABLE #TEMP_DATA (ID INT, VAL NVARCHAR(6),Premium INT)
INSERT INTO #TEMP_DATA
SELECT 1,'CH',201 UNION ALL
SELECT 1,'CH',0 UNION ALL
SELECT 1,'CHH',301 UNION ALL
SELECT 1,'CHS',401 UNION ALL
SELECT 1,'CHHS',501 UNION ALL
SELECT 1,'CHHS',0
SELECT ID,Val,Premium,DENSE_RANK()OVER(ORDER BY Val) RN INTO #T
FROM #TEMP_DATA
SET #Strt=1
SELECT #End= MAX(RN) FROM #T
WHILE #Strt<=#End BEGIN
SELECT #MaxNo=MAX(RN)+1 FROM #T
UPDATE #T SET RN= #MaxNo WHERE Premium=0 AND RN=#Strt
SET #Strt=#Strt+1
END
SELECT * FROM #T
ROLLBACK TRAN

Getting the minimum of column on the basis of other field

I have a scenario wherein I have
Id|rank| date
1 | 7 |07/08/2015
1 | 7 |09/08/2015
1 | 8 |16/08/2015
1 | 8 |17/08/2015
1 | 7 |19/08/2015
1 | 7 |15/08/2015
2 | 7 |01/08/2015
2 | 7 |02/08/2015
2 | 8 |16/08/2015
2 | 8 |17/08/2015
2 | 7 |26/08/2015
2 | 7 |28/08/2015
My desired solution is
1 | 7 |07/08/2015
1 | 8 |16/08/2015
1 | 7 |15/08/2015
2 | 7 |01/08/2015
2 | 8 |16/08/2015
2 | 7 |26/08/2015
i.e for each block of id and rank I want the minimum of date.
I have tried using while loop as there are thousands of records it is taking 2 hours to load.Is there any other way to do please suggest.
For each row give unique row number using necessary order. (As I get Id is more important than date and date is more important than rank).
Join resulting table to itself using row numbers shifted by one row (d1.RowNum = d2.RowNum+1).
Select only rows that are joined to "other block" rows (d1.Id <> d2.Id or d1.Rank <> d2.rank).
Depending on shifting direction and selected table either maximal or minimal date will be selected.
Don't forget "edge case" - row that due to shifting can't be joined (that's why not inner join and d1.RowNum = 1 condition used).
;WITH dataWithRowNums as (
select Id, Rank, Date,
RowNum = ROW_NUMBER() OVER (ORDER BY Id,date,rank)
from YourTable
)
select d1.Id, d1.Rank, d1.Date
from dataWithRowNums d1
left join dataWithRowNums d2
on d1.RowNum = d2.RowNum+1 and (d1.Id <> d2.Id or d1.Rank <> d2.rank)
where not d2.Id is null or d1.RowNum = 1
This code returns result bit different from yours:
Id Rank Date
1 7 2015-08-07
1 8 2015-08-16
1 7 2015-08-19 <-- you've got here 2015-08-15
2 7 2015-08-01
2 8 2015-08-16
2 7 2015-08-26
As block (Rank 8 Id 1) have started at 16/08 so row 15/08 for rank 7 is related to first block (rank7 Id1).
If you still need your sorting (so 15/08 rank 7 is related to second block (rank7 id1)) then you should provide your own RowSorting data and then ask here about another solution for another task )
Here is the query using row_number()
;WITH cte_rec
as (SELECT Id,Rank,Date
,ROW_NUMBER()OVER (partition by Id,Rank ORDER BY date) as RNO
FROM YourTable)
SELECT Id,Rank,Date
FROM cte_rec
WHERE RNO =1
This is what I have tried and is running as expected
create table #temp
(
iden int identity(1,1),
ID int,
[rank] int,
[date] date,
dr_id int,
rownum_id int,
grouprecord int
)
Insert into #temp(id,rank,date)
select 1 , 7 ,'07/08/2015'
union all select 1 , 7 ,'09/08/2015'
union all select 1 , 8 ,'08/16/2015'
union all select 1 , 8 ,'08/17/2015'
union all select 1 , 7 ,'08/19/2015'
union all select 1 , 7 ,'08/15/2015'
union all select 2 , 7 ,'08/01/2015'
union all select 2 , 7 ,'08/02/2015'
union all select 2 , 8 ,'08/16/2015'
union all select 2 , 8 ,'08/17/2015'
union all select 2 , 7 ,'08/26/2015'
union all select 2 , 7 ,'08/28/2015'
update t1
set dr_id = t2.rn
from #temp t1 inner join
(select iden, dense_rank() over(order by id) as rn from #temp) t2
on t1.iden = t2.iden
update t1
set rownum_id = t2.rn
from #temp t1 inner join
(select iden, row_number() over(partition by dr_id order by id) as rn from #temp) t2
on t1.iden = t2.iden
select *,row_number() over(order by iden)rn into #temp1 from
(
select t2.*
from #temp t1 inner join #temp t2
on (t1.dr_id = t2.dr_id or t2.dr_id = (t1.dr_id +1) ) and ( t1.rank<>t2.rank or t2.dr_id = (t1.dr_id +1) )
and t2.iden = t1.iden + 1
)a
declare #id int,#miniden int,#maxiden int,#maxid int
set #id = 1
select #maxid = max(iden) from #temp
while exists(select 1 from #temp1 where rn = #id)
begin
Select #miniden = iden from #temp1
where rn = #id
Select #maxiden = iden from #temp1
where rn = #id+1
update #temp
set grouprecord = #id +1
where iden between #miniden and #maxiden
IF(#maxiden IS NULL)
BEGIN
Update #temp
set grouprecord = #id +1
where iden between #miniden and #maxid
END
set #id = #id + 1
SET #miniden =NULL
SET #maxiden = NULL
end
UPDATE #TEMP
SET GROUPRECORD = 1
WHERE GROUPRECORD IS NULL
select min(date) as mindate,grouprecord from #temp
group by grouprecord
Thanks everyone the help :)

The row must be shown same order in result set according to column value

I am using SQL Server 2008 R2.
I have the following data:
ID Value OrderNumber
1 A NULL
2 E 4
3 C NULL
4 B NULL
5 F 2
6 D NULL
i want to write a query that must fetch data ordering by OrderNumber column considering OrderNumber values. The query result must be below:
ID Value OrderNumber
1 A NULL
5 F 2 --indicates row must be second in result set.
3 C NULL
2 E 4 --indicates row must be fourth in result set.
4 B NULL
6 D NULL
Thanks for reading and your answers.
I've tried a number of different ways, but the only way I can find that produces the required results in a guaranteed way is:
declare #t table (ID int not null,Value char(1) not null,OrderNumber int null)
insert into #T(ID,Value,OrderNumber) values
(1,'A',NULL),
(2,'E',4),
(3,'C',NULL),
(4,'B',NULL),
(5,'F',2),
(6,'D',NULL)
;With Nbrs as (
select ROW_NUMBER() OVER (ORDER BY ID) as n from #t
), AvailableNbrs as (
select n,ROW_NUMBER() OVER (ORDER BY n) as rn from Nbrs where n not in (select OrderNumber from #t where OrderNumber is not null)
), RequiredOrders as (
select ID,ROW_NUMBER() OVER (ORDER BY ID) as rn from #t where OrderNumber is null
)
select
*,COALESCE(OrderNumber,an.n) as FinalOrder
from
#t t
left join
RequiredOrders ro
on
t.ID = ro.ID
left join
AvailableNbrs an
on
ro.rn = an.rn
order by COALESCE(OrderNumber,an.n)
Where we use a few CTEs to find OrderNumbers that aren't currently assigned, and to match those 1-1 with rows which have no OrderNumber.
Results:
|--------- #t --------------| |----- RequiredOrders ---------| |----- AvailableNbrs -------------------| |- COALESCE -------|
ID Value OrderNumber ID rn n rn FinalOrder
----------- ----- ----------- ----------- -------------------- -------------------- -------------------- --------------------
1 A NULL 1 1 1 1 1
5 F 2 NULL NULL NULL NULL 2
3 C NULL 3 2 3 2 3
2 E 4 NULL NULL NULL NULL 4
4 B NULL 4 3 5 3 5
6 D NULL 6 4 6 4 6
Found a much better solution than the accepted answer:
declare #t table(id int, value char, ordernumber int)
insert #t values(1,'A', null)
insert #t values(2,'E',4)
insert #t values(3,'C',NULL)
insert #t values(4,'B',NULL)
insert #t values(5,'F',2)
insert #t values(6,'D',NULL)
;with a as
(
select *, row_number() over (order by id)+.1 rn1 from #t
where ordernumber is null
union all
select *, ordernumber - rank() over (order by ordernumber)+1 rn1 from #t
where ordernumber is not null
)
select * from a order by rn1, ordernumber
Try this:
SELECT *
FROM tableName
Order BY CASE WHEN OrderNumber IS NULL THEN ID ELSE OrderNumber END