parent id hierarchy identification MS SqlServer2012 - sql-server

I have this code
create table #temp
(
order_id int not null identity(1,1) primary key
,sid int
,created_date date
,parent_order_id int
)
insert into #temp
(
sid
,created_date
)values(1,'2017-01-01')
insert into #temp
(
sid
,created_date
,parent_order_id
)values(1,'2017-02-01',1),(1,'2017-03-01',2),(1,'2017-04-01',3)
insert into #temp
(
sid
,created_date
)values(1,'2017-06-01')
insert into #temp
(
sid
,created_date
,parent_order_id
)values(1,'2017-07-01',5),(1,'2017-08-01',6)
select * from #temp
Whenever parent_order_id is null which indicates it is a new order. After that customer can add items associated to that order. so we have parent_order_id filled for these associations. But I want to know what is the first order_id for each association child order.I am looking for an output like below.
`order_id sid created_date parent_order_id original_order_id
1 1 2017-01-01 NULL 1
2 1 2017-02-01 1 1
3 1 2017-03-01 2 1
4 1 2017-04-01 3 1
5 1 2017-06-01 NULL 4
6 1 2017-07-01 5 4
7 1 2017-08-01 6 4
`
any help is appreciated. Thanks in advance.

With the following piece of code you can get results you are expecting.
;WITH cte (order_id, original_order_id)
AS
(
SELECT order_id, order_id AS original_order_id
FROM #temp WHERE parent_order_id IS NULL
UNION ALL
SELECT o.order_id AS order_id, cte.original_order_id AS original_order_id
FROM #temp AS o
JOIN cte
ON o.parent_order_id = cte.order_id
)
SELECT #temp.order_id, #temp.sid, #temp.created_date, #temp.parent_order_id, cte.original_order_id
FROM #temp
JOIN cte ON cte.order_id=#temp.order_id
ORDER BY cte.order_id
Please be aware, that there are certain limits on recursion as this for CTE. Currently it is 100 which can be pushed up to 32767.

Related

How do you dynamically Assing Unique Code for every row iin hierarchical table?

My table.
Table1
Id ParentId Name Code
1 Null John
2 1 Harry
3 1 Mary
4 2 Emma
5 2 Kyle
6 4 Robert
7 Null Rohit
I want to assign each individual with the the following format unique hierarchy codes
Output Required
Id ParentId Name Code
1 Null John 1
2 1 Harry 1.1
3 1 Mary 1.2
4 2 Emma 1.1.1
5 2 Kyle 1.1.2
6 4 Robert 1.1.1.1
7 Null Rohit 2
and so on.
I hope I've got this correctly...
You can use a recursive CTE together with ROW_NUMBER() in order to create your codes.
DECLARE #dummy TABLE(Id INT,ParentId INT,[Name] VARCHAR(100));
INSERT INTO #dummy(Id,ParentId,[Name]) VALUES
(1,Null,'John')
,(2,1 ,'Harry')
,(3,1 ,'Mary')
,(4,2 ,'Emma')
,(5,2 ,'Kyle')
,(6,4 ,'Robert')
,(7,Null,'Rohit');
WITH recCTE AS
(
SELECT Id,ParentId,[Name]
,CONCAT(N'.',CAST(ROW_NUMBER() OVER(ORDER BY Id) AS NVARCHAR(MAX))) AS Code
FROM #dummy WHERE ParentId IS NULL
UNION ALL
SELECT d.Id,d.ParentId,d.[Name]
,CONCAT(r.Code,N'.', ROW_NUMBER() OVER(ORDER BY d.Id))
FROM #dummy d
INNER JOIN recCTE r ON d.ParentId=r.Id
)
SELECT Id,ParentId,[Name]
,STUFF(Code,1,1,'') AS Code
FROM RecCTE;
The idea in short:
We pick the rows with ParentId IS NULL and give them a running number.
Now we go iteratively through them (it's a hidden RBAR actually) and call their children, again with a running number.
This we do until there is nothing left.
the final SELECT needs a STUFF in order to to get rid of the first dot.
And with an extension like this, you can create an alphanumerically sortable code:
WITH recCTE AS
(
SELECT Id,ParentId,[Name]
,CONCAT(N'.',CAST(ROW_NUMBER() OVER(ORDER BY Id) AS NVARCHAR(MAX))) AS Code
,CONCAT(N'000',CAST(ROW_NUMBER() OVER(ORDER BY Id) AS NVARCHAR(MAX))) AS Code2
FROM #dummy WHERE ParentId IS NULL
UNION ALL
SELECT d.Id,d.ParentId,d.[Name]
,CONCAT(r.Code,N'.', ROW_NUMBER() OVER(ORDER BY d.Id))
,CONCAT(r.Code2,RIGHT(CONCAT('0000',ROW_NUMBER() OVER(ORDER BY d.Id)),4))
FROM #dummy d
INNER JOIN recCTE r ON d.ParentId=r.Id
)
SELECT Id,ParentId,[Name]
,STUFF(Code,1,1,'') AS Code
,Code2
FROM RecCTE
ORDER BY Code2;

Group by with further grouping

I have the following sample data:
ID | SectionID | LocID
1 32 12
1 32 2
1 32 2
1 34 3
1 34 4
2 36 8
2 36 9
2 37 8
2 37 9
2 37 4
The output should be grouped by ID. The Count LocID field should show the
number of DISTINCT LocIDs per sectionID totaled together.
For ID of 1, we have 2 distinct LocID for SectionID 32 and 2 for SectionID 34. Totaled equals 4
For ID of 2, we have 2 distinct LocID for SectionID 36 and 3 for sectionID 37. Total equals 5
Result:
ID Count
1 4
2 5
I did a group by ID but not sure how to do further grouping based on what I need. I am using SQL Server 2016.
The easiest way, I think, is to group by your ID and do some kind of count distinct on a concatenation of SectionID and LocID. If these are character data, you can get away with just concatenating with some kind of delimiter. If their numeric, you can do something like the example below, or convert them to strings and concat with a delimiter.
-------------------------
-- set up sample data
-------------------------
declare #datatable as table(ID int, SectionID int, LocID int)
insert into #datatable(ID, SectionID, LocID) VALUES
(1,32,12 ),
(1,32,2 ),
(1,32,2 ),
(1,34,3 ),
(1,34,4 ),
(2,36,8 ),
(2,36,9 ),
(2,37,8 ),
(2,37,9 ),
(2,37,4 )
-------------------------
-- The query
-------------------------
SELECT
ID
,COUNT (DISTINCT SectionID * 10000 + LocID)
FROM
#datatable
GROUP BY ID
Gives the result:
(10 row(s) affected)
ID
----------- -----------
1 4
2 5
(2 row(s) affected)
You could use a nested group by, such as
SELECT ID, SUM([Count])
FROM
(
SELECT ID, SectionID, COUNT(DISTINCT LocID) AS [Count]
FROM Table
GROUP BY ID, SectionID
) Q
GROUP BY ID
A down and dirty way is to just nest your groupings.
DECLARE #t TABLE
(
ID INT,
SectionID INT,
LocID INT
);
INSERT INTO #t
(
ID
,SectionID
,LocID
)
VALUES
( 1,32,12),
( 1,32,2),
( 1,32,2),
( 1,34,3),
( 1,34,4),
( 2,36,8),
( 2,36,9),
( 2,37,8),
( 2,37,9),
( 2,37,4)
SELECT
d.ID
,SUM(d.LocIDs) AS LocIDCnt
FROM
(
SELECT
ID
,SectionID
,COUNT(DISTINCT LocID) AS LocIDs
FROM
#t
GROUP BY
ID
,SectionID
) AS d
GROUP BY
d.ID;
Result set:
+----+-------+
| ID | Count |
+----+-------+
| 1 | 4 |
| 2 | 5 |
+----+-------+
One more way:
select ID, COUNT(*) as SecLocCount
from (
select distinct ID, SectionID, LocID from [MyTable]
) AS distinctRows
group by ID

How to retrieve row when null then take above record in table,with out using loop and update

id name
--------------
1 ACTIVE
2 NULL
3 NULL
4 NULL
5 COMPLETED
6 NULL
7 COMPLETED
8 COMPLETED
9 ACTIVE
10 NULL
11 ACTIVE
Output:
id name
--------------
1 ACTIVE
2 ACTIVE
3 ACTIVE
4 ACTIVE
5 COMPLETED
6 COMPLETED
7 COMPLETED
8 COMPLETED
9 ACTIVE
10 ACTIVE
11 ACTIVE
Task: retrieve null records with above values without using a loop and update.
in will come select statement.
With DDL and Sample data:
CREATE TABLE #Sample (ID int, [Name] varchar(9));
INSERT INTO #Sample
VALUES (1,'ACTIVE'),
(2,NULL),
(3,NULL),
(4,NULL),
(5,'COMPLETED'),
(6,NULL),
(7,'COMPLETED'),
(8,'COMPLETED'),
(9,'ACTIVE'),
(10,NULL),
(11,'ACTIVE');
GO
SELECT *
FROM #Sample;
UPDATE S
SET [Name] = (SELECT TOP 1 [Name]
FROM #Sample sq
WHERE sq.ID < S.ID
AND sq.[Name] IS NOT NULL
ORDER BY sq.ID DESC)
FROM #Sample S
WHERE S.[Name] IS NULL;
SELECT *
FROM #Sample;
GO
DROP TABLE #Sample;
Assuming window functions are supported in your version of SQL Server, this can be done with classifying consecutive nulls in the name column to the same group as the first occurring name for an id. Then use max to get the name for the group and use it to update.
with cte as (
select id,name,max(name) over(partition by grp) as new_name
from (select *, sum(case when name is null then 0 else 1 end) over(order by id) as grp
from tbl
) t
)
update cte set name = new_name
where name is null;

count detail record and display in new column in sql server

i am using sql server 2008, in which i have some trouble i can not find one column
TblMaster
ID Name City
1 Hiren Juanagadh
2 Ashish Gandhinagar
2 Mayur Ahmedabad
3 Hitesh Junagadh
4 Nipun Ahmedabad
4 Vivek Rajkot
4 Samir Surat
5 Sagar Vadodara
Now i want Anoter column CountId so i want output like below
TblMaster
ID Name City CountId
1 Hiren Juanagadh 0
2 Ashish Gandhinagar 2
2 Mayur Ahmedabad 2
3 Hitesh Junagadh 0
4 Nipun Ahmedabad 3
4 Vivek Rajkot 3
4 Samir Surat 3
5 Sagar Vadodara 0
Means if Id column only one then CountId = 0
If Id column more than one then CountId = Count of Idcolumn
Prepare table
declare #T table (
id int,
Name nvarchar(6),
City nvarchar(20))
insert #T values
( 1 , 'Hiren', 'Juanagadh'),
( 2 , 'Ashish', 'Gandhinagar'),
( 2 , 'Mayur', 'Ahmedabad'),
( 3 , 'Hitesh', 'Junagadh'),
( 4 , 'Nipun', 'Ahmedabad'),
( 4 , 'Vivek', 'Rajkot'),
( 4 , 'Samir', 'Surat'),
( 5 , 'Sagar', 'Vadodara')
Select statement
without 1->0 correction
SELECT *, CountID = count(*) over (Partition by ID)
from #T
with 1->0 correction
select id, Name,City,CountID = case when CountID = 1 then 0 else CountID end
from (
SELECT *, CountID = count(*) over (Partition by ID)
from #T )
RES
Try this query:::
select *,(case when (select count(id) from TblMaster )=1
then 0 else (select count(id) from TblMaster) end) as count
from tblmaster

Help with avoiding CURSOR for Column Calculation

I have a bunch of records in a table variable like so:
Id ProductId Rank RankCreated
1 123213 2 2011-05-02
2 123213 4 2011-05-03
3 123213 1 2011-05-03
4 155432 10 2011-05-01
5 155432 10 2011-05-02
Id is an identity column i added to my table variable (will explain why i need it in a moment). ProductId is a Product. Rank is a value which represents a product's rank at a given time. RankCreated is the time that Product was ranked.
What im trying to do:
Calculate the "movement" between each product rank, for each product. Where "movement" is defined as current - previous.
So the "computed column" would look like this:
Id ProductId Rank RankCreated Movement
1 123213 2 2011-05-02 NULL
2 123213 4 2011-05-03 2
3 123213 1 2011-05-03 -3
4 155432 10 2011-05-01 NULL
5 155432 10 2011-05-02 0
I added the Id column so i could use that to fetch the previous record.
Here's how i got the data into the temp table:
insert into #rankhistories (productid, [rank], [rankcreated])
select a.ProductId, b.[rank]
from dbo.ProductRankHistories b
inner join dbo.Products a on a.ProductId = b.ProductId
order by a.ProductId, b.RankCreated
I really can't see how i can avoid a cursor here. There are 6000+ records in that table variable, and with my cursor solution it took 5 seconds, which isn't acceptable.
Can anyone help?
DECLARE #TV TABLE
(
Id INT IDENTITY(1,1) PRIMARY KEY,
ProductId INT,
Rank INT,
RankCreated DATE
)
/*Populate *6000 rows of random data*/
INSERT INTO #TV
SELECT TOP 6000
ROW_NUMBER() OVER (ORDER BY (SELECT 0)) / 9 AS ProductId,
CRYPT_GEN_RANDOM(1) % 10 AS Rank,
GETDATE() AS RankCreated
FROM master..spt_values v1,master..spt_values v2
SELECT t1.Id,
t1.ProductId,
t1.Rank,
t1.RankCreated,
t2.Rank - t1.Rank AS Movement
FROM #TV t1
LEFT MERGE JOIN #TV t2 ON t1.Id = t2.Id+1 AND t1.ProductId=t2.ProductId
ORDER BY t1.Id

Resources