Hierarchical data run infinitely in SQL Server 2008 - sql-server

I am using SQL Server 2008.In my databse one table like:
------------------------------
id parentId name
------------------------------
1 NULL india
2 1 gujrat
3 1 Maharastra
4 1 rajsthan
5 2 ahmedabad
6 2 rajkot
7 NULL USA
8 7 newyork
1 3 mumbai
1 3 goa
1 4 jaipur
1 7 californiya
Here i want to get this data with it's level in hierarchy, so i create query like:
with RecursiveTable_CTE (Id,Parentid,Name_cte,Hlevel)
as
(
select id , parentId, name, 0 as Hlevel from treeTable where id = 1
union all
select t.id,t.parentId,t.name, Hlevel + 1 as LEVEL from treeTable as t inner join RecursiveTable_CTE as rtc on t.id = rtc.Parentid
)
select * from RecursiveTable_CTE option (maxrecursion 0)
and also try
with RecursiveTable_CTE (Id,Parentid,Name_cte,Hlevel)
as
(
select id , parentId, name, 0 as Hlevel from treeTable where parentId is null
union all
select t.id,t.parentId,t.name, Hlevel + 1 as LEVEL from treeTable as t inner join RecursiveTable_CTE as rtc on rtc.id = t.parentId
)
select * from RecursiveTable_CTE option (maxrecursion 0)
but both result infinite loop.
can any one help me?

I think the query you're after is this:
with RecursiveTable_CTE (Id,Parentid,Name_cte,Hlevel)
as
(
select id , parentId, name, 0 as Hlevel
from treeTable
where parentId is null
union all
select t.id,t.parentId,t.name, rtc.Hlevel + 1 as Hlevel
from treeTable as t
inner join RecursiveTable_CTE as rtc on t.Parentid = rtc.id
)
select * from RecursiveTable_CTE
The problem is though that I think your data is not correct and id and parentId are swapped in some rows. I used this data:
insert into treeTable values (1, NULL, 'india')
insert into treeTable values (2, 1, 'gujrat')
insert into treeTable values (3, 1, 'Maharastra')
insert into treeTable values (4, 1, 'rajsthan')
insert into treeTable values (5, 2, 'ahmedabad')
insert into treeTable values (6, 2, 'rajkot')
insert into treeTable values (7, NULL, 'USA')
insert into treeTable values (8, 7 , 'newyork')
insert into treeTable values (9, 1 , 'mumbai')
insert into treeTable values (10, 1 , 'goa')
insert into treeTable values (11, 1 , 'jaipur')
insert into treeTable values (12, 7 , 'californiya')
SQL Fiddle demo

Here in your case ther 1 is top level but again 1 has parents 3,4,7.
your infinite loop is due to your wrong herarchy.
id parentId name
------------------------------
1 NULL india
1 3 mumbai
1 4 jaipur
1 7 californiya

You have duplicates in the ID column, Is that a typo? If you don't have duplicates
CREATE TABLE states
(
[id] INT,[parentid] INT,[name] VARCHAR(11)
);
INSERT INTO states
([id],[parentid],[name])
VALUES (1,NULL,'india'),
(2,1,'gujrat'),
(3,1,'Maharastra'),
(4,1,'rajsthan'),
(5,2,'ahmedabad'),
(6,2,'rajkot'),
(7,NULL,'USA'),
(8,7,'newyork'),
(9,3,'mumbai'),
(10,3,'goa'),
(11,4,'jaipur'),
(12,7,'californiya');
WITH recursivetable_cte (id, parentid, name_cte, hlevel)
AS (SELECT id,parentid,name,0 AS Hlevel
FROM states
WHERE parentid IS NULL
UNION ALL
SELECT t.id,t.parentid,t.name,hlevel + 1 AS LEVEL
FROM states AS t
INNER JOIN recursivetable_cte AS rtc
ON t.parentid = rtc.id)
SELECT *
FROM recursivetable_cte
OPTION (maxrecursion 0)

#Szymom is right.id should be unique.though in this query no need of changing table data
see the change,
;with RecursiveTable_CTE (Id,Parentid,Name_cte,Hlevel)
as
(
select id , parentId, name, 0 as Hlevel from #t where id = 1 **and parentId is null**
union all
select t.id,t.parentId,t.name, Hlevel+ 1 as LEVEL from #t as t
inner join RecursiveTable_CTE as rtc on t.Parentid = rtc.id **and t.id<>1**
)
select * from RecursiveTable_CTE

Related

How to optimize the query so the manual work can be dyanmic and in optimized way

I have below table like: SQL fiddle
I am able to get this output via XML, but I am not sure how I can get below output properly for larger number of users (approx 0.2M users).
Later I want to get top-3 Names by their counts for each id ,so RANK or OrderBy clauses will come into SQL and not sure how many iteration will it take when data is of large number of users.
Working code that I have tried:
-----------SQL Raw Table Creation------------------------
CREATE TABLE tb
(
Id INT,
Name VARCHAR(50) NOT NULL
);
INSERT INTO tb (Id, Name) VALUES (1, 'aa');
INSERT INTO tb (Id, Name) VALUES (1, 'aa');
INSERT INTO tb (Id, Name) VALUES (1, 'aa');
INSERT INTO tb (Id, Name) VALUES (1, 'aa');
INSERT INTO tb (Id, Name) VALUES (1, 'aa');
INSERT INTO tb (Id, Name) VALUES (1, 'bb');
INSERT INTO tb (Id, Name) VALUES (1, 'cc');
INSERT INTO tb (Id, Name) VALUES (1, 'cc');
INSERT INTO tb (Id, Name) VALUES (1, 'dd');
INSERT INTO tb (Id, Name) VALUES (1, 'dd');
INSERT INTO tb (Id, Name) VALUES (1, 'dd');
INSERT INTO tb (Id, Name) VALUES (2, 'aa');
INSERT INTO tb (Id, Name) VALUES (2, 'bb');
INSERT INTO tb (Id, Name) VALUES (2, 'bb');
INSERT INTO tb (Id, Name) VALUES (2, 'ee');
INSERT INTO tb (Id, Name) VALUES (2, 'ee');
INSERT INTO tb (Id, Name) VALUES (2, 'ee');
INSERT INTO tb (Id, Name) VALUES (2, 'ee');
INSERT INTO tb (Id, Name) VALUES (3, 'aa');
INSERT INTO tb (Id, Name) VALUES (3, 'bb');
INSERT INTO tb (Id, Name) VALUES (3, 'cc');
INSERT INTO tb (Id, Name) VALUES (3, 'dd');
INSERT INTO tb (Id, Name) VALUES (3, 'dd');
INSERT INTO tb (Id, Name) VALUES (3, 'dd');
-----------------Want to RANK or get only top 3 rows for each Id when group by Name--------------
select f.* into #t1
from(
select f.*
from(
select f.*
from (
select top 3 id,name,count(name) as total
from tb
where id = 1
group by id,name
order by id,total desc
)f
Union
select top 3 id,name,count(name) as total
from tb
where id = 2
group by id,name
order by id,total desc
)f
Union
select top 3 id,name,count(name) as total
from tb
where id = 3
group by id,name
order by id,total desc
) f
/* Output is moved in temp table #t1 which looks like
id name total
1 aa 5
1 cc 2
1 dd 3
2 aa 1
2 bb 2
2 ee 4
3 bb 1
3 cc 1
3 dd 3
*/
---------Final Joining for each Top3Names and RespectiveTotal -----
select a.id as ID, a.listStr as Top3Names , b.Total as RespectiveTotal
from
(
SELECT id,STUFF((SELECT ',' + name
FROM #t1 EE
WHERE EE.id=E.id
FOR XML PATH('')), 1, 1, '') AS listStr
FROM #t1 E
GROUP BY E.id
)a
left Join
(
SELECT id,STUFF((SELECT ',' + cast(total as Varchar)
FROM #t1 EE
WHERE EE.id=E.id
FOR XML PATH('')), 1, 1, '') AS Total
FROM #t1 E
GROUP BY E.id
)b
on a.id=b.id
Output:
ID Top3Names RespectiveTotal
1 aa,cc,dd 5,2,3
2 aa,bb,ee 1,2,4
3 bb,cc,dd 1,1,3
Here I am using UNION for each ID, which is not correct way of doing. I want an optimized way. Also I am using a temp table to store my results. Is it a good way? Let me know for any correct solution or alternatives so that I can test it on larger.
on my SQL SERVER machine for given sample data, your query stats looks like this:
Total Logical Reads: 13
Total CPU Time: 00:00:00.007
if you are using SQL SERVER 2017+ you can use STRING_AGG function :
SELECT
id
, STRING_AGG(name,',') WITHIN GROUP (order by name asc) Top3Names
, STRING_AGG(countx,',') WITHIN GROUP (order by name asc) RespectiveTotal
FROM (
SELECT
id
, name
, count(*) countx
, ROW_NUMBER() over (partition by id order by count(*) desc) rownumber
FROM tb
GROUP BY name, id
) result1
WHERE
result1.rownumber < 4
GROUP BY id
Stats are like :
Total Logical Reads: 1
Total CPU Time: 00:00:00.000
for SQL SERVER 2016- :
select id
, STUFF((
SELECT ',' + t1.Name
FROM cte t1
WHERE t1.id = t2.id
and t1.rownumber < 4
ORDER BY t1.name
FOR XML PATH('')), 1, LEN(','), '') AS Top3Names
, STUFF((
SELECT ',' + cast(t1.countx as varchar(50))
FROM cte t1
WHERE t1.id = t2.id --and t1.name = t2.name
and t1.rownumber < 4
ORDER BY t1.name
FOR XML PATH('')), 1, LEN(','), '') AS RespectiveTotal
from cte t2
group by id
Stats are like :
Total Logical Reads: 7
Total CPU Time: 00:00:00.006
so regardless of sql server version, It will improve performance, you will get the best performance if you are using sql server 2017 or above using query above.

Add incrementally row values of a column of type string

I have a table with the following values
UserID ParentID Levels Path
1 NULL 0 A1
5 1 1 A2
9 5 2 A3
43 9 3 A4
The output should be like followed :
UserID ParentID Levels FinalPath
1 NULL 0 A1/
5 1 1 A1/A2/
9 5 2 A1/A2/A3/
43 9 3 A1/A2/A3/A4/
Thanks in advance for any guidance on this.
Solution using a recusive common table expression.
Sample data
create table users
(
userid int,
parentid int,
levels int,
path nvarchar(100)
);
insert into users (userid, parentid, levels, path) values
(1, NULL, 0, 'A1'),
(5, 1, 1, 'A2'),
(9, 5, 2, 'A3'),
(43, 9, 3, 'A4');
Solution
with cte as
(
select u.userid, u.parentid, u.levels, u.path
from users u
where u.parentid is null
union all
select u.userid, u.parentid, u.levels, convert(nvarchar(100), c.path + '/' + u.path)
from users u
join cte c
on c.userid = u.parentid
)
select c.userid, c.parentid, c.levels, c.path + '/' as FinalPath
from cte c;
Fiddle
Here's a version that both calculates the Level and appends the Path.
Data
drop table if exists dbo.test_table;
go
create table dbo.test_table(
UserID int,
ParentID int,
[Path] varchar(5));
insert dbo.test_table([UserID], [ParentID], [Path]) values
(1,null, 'A1'),
(5,1, 'A2'),
(9,5, 'A3'),
(43,9, 'A4');
Query
;with recur_cte([UserId], [ParentID], h_level, [Path]) as (
select [UserId], [ParentID], 0, cast([Path] as varchar(100))
from dbo.test_table
where [ParentID] is null
union all
select tt.[UserId], tt.[ParentID], rc.h_level+1, cast(concat(tt.[Path], '/', rc.[Path]) as varchar(100))
from dbo.test_table tt join recur_cte rc on tt.[ParentID]=rc.[UserId])
select * from recur_cte;
Results
UserId ParentID h_level Path
1 NULL 0 A1
5 1 1 A1/A2
9 5 2 A1/A2/A3
43 9 3 A1/A2/A3/A4

How do I get a list of corporations where corporation (ID) is included?

IMPORTANT ... in my RL project, the ID's aren't INT, but GUID, so my data is NOT hierarchical!
I have a table with companies and a table with links between companies.
I need to be able to retrieve a list of companies from a specific company ID.
Here's my test code...
CREATE TABLE ##corporations
(
CorporationID INT NOT NULL,
CorporationName NVARCHAR(20) NOT NULL
);
CREATE TABLE ##corporationLinks
(
FromCorporationID INT NOT NULL,
ToCorporationID INT NOT NULL
);
INSERT INTO ##corporations (CorporationID, CorporationName) VALUES (1, 'Nike')
INSERT INTO ##corporations (CorporationID, CorporationName) VALUES (2, 'Cocal Cola')
INSERT INTO ##corporations (CorporationID, CorporationName) VALUES (3, 'Apple')
INSERT INTO ##corporations (CorporationID, CorporationName) VALUES (4, 'Google')
INSERT INTO ##corporations (CorporationID, CorporationName) VALUES (5, 'Amazon')
INSERT INTO ##corporations (CorporationID, CorporationName) VALUES (6, 'Samsung')
INSERT INTO ##corporationLinks (FromCorporationID, ToCorporationID) VALUES (1, 2)
INSERT INTO ##corporationLinks (FromCorporationID, ToCorporationID) VALUES (2, 3)
INSERT INTO ##corporationLinks (FromCorporationID, ToCorporationID) VALUES (4, 5)
INSERT INTO ##corporationLinks (FromCorporationID, ToCorporationID) VALUES (4, 6)
SELECT * FROM ##corporationLinks WHERE FromCorporationID = 2 OR ToCorporationID = 2
/**
Organisations (##corporationLinks) are...
Nike + Coca Cola + Apple + Marcy
and...
Google + Amazon + Samsung
**/
-- How do I eg get a list of companies where Coca Cola is in ... that is where FromCorporationID = 2 OR ToCorporationID = 2 ... result should be Nike, Coca Cola and Apple?
-- How do I eg get a list of companies where Samsung is in ... that is where FromCorporationID = 6 OR ToCorporationID = 6 ... result should be Google, Amazon Cola and Samsung?
DROP TABLE ##corporationLinks
DROP TABLE ##corporations
UDPATE:
If I need to find corporations where Coca Cola is part of then I would do the fowlling...
SELECT * FROM ##corporationLinks WHERE FromCorporationID = 2 OR ToCorporationID = 2
Then I will get 2 results...
FromCorporationID ToCorporationID
-----------------------------------
1 2
2 3
Here after I need to look into which corporations the result is part of...
SELECT * FROM ##corporationLinks WHERE FromCorporationID = 1 OR ToCorporationID = 1
SELECT * FROM ##corporationLinks WHERE FromCorporationID = 3 OR ToCorporationID = 3
Then I will get one more corporation (7):
FromCorporationID ToCorporationID
-----------------------------------
3 7
And then I need to dive into which corporations that is linked with 7...
SELECT * FROM ##corporationLinks WHERE FromCorporationID = 7 OR ToCorporationID = 7
And dive into that result too (guess it is called recursive). etc.
UPDATE 2:
I've updated my sample above to add one more company that should be returned if search is Coca Cola.
Expected result from the query (Coca Cola) above:
CorporationID:
--------------
2
1
3
7
With a recursive CTE:
with
root as (select CorporationID id from companies where CorporationName = 'Coca Cola'),
cte as (
select
case when r.id = cl.FromCorporationID then cl.ToCorporationID else cl.FromCorporationID end id,
case when r.id = cl.FromCorporationID then cl.FromCorporationID else cl.ToCorporationID end otherid
from corporationLinks cl inner join root r
on r.id in (cl.FromCorporationID, cl.ToCorporationID)
union all
select
case when c.id = cl.FromCorporationID then cl.ToCorporationID else cl.FromCorporationID end id,
case when c.id = cl.FromCorporationID then cl.FromCorporationID else cl.ToCorporationID end otherid
from corporationLinks cl inner join cte c
on c.id in (cl.FromCorporationID, cl.ToCorporationID)
and c.otherid not in (cl.FromCorporationID, cl.ToCorporationID)
)
select id CorporationID from root
union all
select id from cte
See the demo.
Results:
> | CorporationID |
> | ------------: |
> | 2 |
> | 1 |
> | 3 |
> | 7 |
left join the ##companies, include its CorporationID or you can include the companyname in the query then group by CorporationID you should be able to see which company it belongs to
Just use a recursive CTE
WITH cte
AS
(
SELECT l1.FromCorporationID,l1.ToCorporationID
FROM ##corporationLinks l1
UNION ALL
SELECT cte.FromCorporationID,l3.ToCorporationID
FROM ##corporationLinks l3
JOIN cte
ON cte.ToCorporationID = l3.FromCorporationID
)
SELECT cte.ToCorporationID--parent
FROM cte
WHERE cte.FromCorporationID =2
UNION
SELECT cte.FromCorporationID--child
FROM cte
WHERE cte.ToCorporationID =2
UNION
SELECT c.CorporationID--self
FROM ##corporations c
WHERE c.CorporationID = 2

Select alternate rows from SQL Server table

I am working with SQL Server 2008. I have a table which does not contain any unique columns; how to get alternate rows from it?
SQL Server table:
+-----+--------+
| id | name |
|-----+--------|
| 1 | abc |
| 2 | pqr |
| 2 | pqr |
| 3 | xyz |
| 4 | lmn |
| 5 | efg |
| 5 | efg |
+-----+--------+
As we've to come with at least one working suggestion with the question, I've tried below code; which is not so proper technique when fetching from a huge amount of data.
Trial:
create table #tmp
(
id int, name varchar(10), srNo int
)
insert into #tmp
select
id, name,
ROW_NUMBER() OVER (ORDER BY id) % 2 as srNo --,alternate rows
from
Employee
select *
from #tmp
where srNo = 1 --or srNo = 0
Above query gives out alternate rows i.e. 1st, 3rd, 5th OR 2nd, 4th, 6th etc.
Please help me out with proper way without #tmp to achieve the goal!
You can just use your select statement as an in-line view. You don't need the #tmp table.
select t.id, name
from (select id, name, ROW_NUMBER() over (order by id) as srNo from Employee) t
where (t.srNo % 2) = 1
SqlFiddle
--To fetch ALTERNATE records from a table (EVEN NUMBERED)
Select * from TableName where ColumnName % 2 = 0
For Eg : select * from HumanResources.Employee where BusinessEntityID % 2 = 0
--To fetch ALTERNATE records from a table (ODD NUMBERED)
Select * from TableName where ColumnName % 2 = 1
For Eg : select * from HumanResources.Employee where BusinessEntityID % 2 = 1
I'm taking student as a table name.
Here is my answer ->
For Even Row Number -
> SELECT id from (SELECT rowno, id from student) where mod(rowno,2)=0
For Odd Row Number -
> SELECT id from (SELECT rowno, id from student) where mod(rowno,2)=1
Same also can be achieved using having clause; but it adds group by task:
SELECT id, name
FROM (SELECT id, name, ROW_NUMBER()over(order by id) AS srNo FROM Employee) x
GROUP BY srNo, id, name
HAVING (srNo % 2) = 0
You can just use your select statement as an in-line view. You don't need the #tblCities table.
select tbl1.CityID,tbl1.CityName from (select ROW_NUMBER() over(order by CityID asc) as row_no,CityID,CityName from tblCities) as tbl1 where tbl1.row_no%2=1
declare #t table
(
id int,
name nvarchar(20)
)
insert into #t
Select 1, 'abc'
union all
Select 2, 'pqr'
union all
Select 2, 'pqr'
union all
Select 3, 'xyz'
union all
Select 4, 'lmn'
union all
Select 5, 'efg'
union all
Select 2, 'efg'
Select * from(
Select *, row_number() over(order by id) as rnum from #t ) t where rnum % 2 <> 0
create table t (id bigint NOT NULL, input_1 boolean not null, data_gps timestamp(0) not null);
insert into t (id, input_1,data_gps) values
(1, false , '2022-01-01 15:42:07'),
(2, true , '2022-01-02 15:42:07'),
(3, true , '2022-01-03 15:42:07'),
(4, false , '2022-01-04 15:42:07'),
(5, true , '2022-01-05 15:42:07'),
(6, true , '2022-01-06 15:42:07'),
(7, true , '2022-01-07 15:42:07'),
(8, true , '2022-01-08 15:42:07'),
(9, false , '2022-01-09 15:42:07'),
(10 ,true , '2022-01-10 15:42:07'),
(11, true , '2022-01-11 15:42:07'),
(12, true , '2022-01-12 15:42:07'),
(13, false , '2022-01-13 15:42:07'),
(14, true , '2022-01-14 15:42:07');
you will have
Here is the query that will group by value change
select input_1, min(data_gps) as mind, max(data_gps) as maxd
from (
select input_1, data_gps,
row_number() over (order by data_gps)
- row_number() over (partition by input_1 order by data_gps) as grp
from t
) as tmp
group by input_1, grp
order by min(data_gps);
The results
DEMO
https://dbfiddle.uk/6Ajy3H5O

How to find the parent ID in a return table

I have following table:
ID ParentID
1 NULL
2 1
3 2
4 NULL
5 4
6 5
7 3
I want to find the first ID of a specific child ID.
Example: ID=7 and the result is 1
ID=6 and the result is 4
How to do it?
You need to do a bit of recursive CTE magic to solve this one.
Given the data in a table variable thusly:
declare #data table(id int, parentid int)
insert into #data
select 1, null
union select 2,1
union select 3,2
union select 4, null
union select 5,4
union select 6,5
union select 7,3
The following should do the trick:
;with recursiveTable as
(
select d.id, d.parentId, 0 as depth
from #data d
where d.id=6 -- Parameterize this
union all
select d.id, d.parentid, r.depth-1
from #data d
inner join recursiveTable r
on d.id = r.parentId
)
select top 1 id
from recursiveTable
order by depth
Plugging 6 in as above returns 4. Changing this to 7 returns 1 as requested.
Try this:
CREATE TABLE childpar(ID int,ParentID int)
INSERT INTO childpar
values(1,NULL),
(2, 1),
(3, 2),
(4, NULL),
(5, 4),
(6, 5),
(7, 3)
DECLARE #inpyID int
SET #inpyID=7
;WITH CTE1 as (
select * from childpar where id=#inpyID
union all
select c2.* from CTE1 c1 inner join childpar c2 on c1.ParentID = c2.ID
)
select top 1 id from CTE1 order by id asc

Resources