Select hierarchy from table - sql

I have a table ServiceItem that has Id, ParentId and some properties like Name, Description and so on. Max level of hierarchy is 2. I need a query that selects rows with some condition, for example Name = '123' and its parent row to get smth like:
Id ParentId Name
1 NULL '12'
2 1 '123'
I have tried this:
SELECT
*
FROM ServiceItem si
WHERE si.Name = '123'
OR EXISTS (
SELECT
*
FROM ServiceItem syst
JOIN ServiceItem si2
ON si2.ParentId = syst.Id
WHERE syst.Id = si.ParentId
AND si2.Name = '123'
)
But it returns parent and all of it's children. Is there any chance to do it with one query? I'm using T-SQL to do it.
It's differs from this question because i need to get a bunch of rows, not only Id by path and conditions in my query could be different.

You can use a Common Table Expression with recursion:
WITH cte AS
(
SELECT *
FROM ServiceItem
WHERE Name = '123'
UNION ALL
SELECT *
FROM ServiceItem si
INNER JOIN cte
ON cte.ParentId = si.Id
)
SELECT * FROM cte
For a more in-depth example, see this Q&A

WITH cte AS
(
SELECT *
FROM ServiceItem
WHERE Name = '123'
UNION ALL
SELECT *
FROM ServiceItem si
INNER JOIN cte
ON cte.ParentId = si.Id
)
SELECT * FROM cte
It's a good query, but I also found this one:
SELECT
*
FROM ServiceItem si
WHERE si.Name = '123'
OR EXISTS (
SELECT
*
FROM ServiceItem si2
WHERE si2.Name = '123'
and si2.ParentId = si.Id
)

Related

Many to Many with multiple Ands

I am trying a simple (or not) query to get Users that exist in 2 Departments.
Struct:
User
ID
Name
UserDepartment
ID
IDUser
IDDepartment
Department
ID
Name
So i want users from DepartmentA and DepartmentB (
Is imposible to do:
Select * from User as US
left join UserDepartment as DP on User.ID = UserDepartment.IDUser
where DP.IDDepartment = 1 and DP.IDDepartment = 2
Zero results...
An query to resolve this is sothing like:
select * from UserDepartment
where IDDepartment in (1,2)
group by IDUser
having COUNT(*)=2
But is this the only solution? there are other easy queries out there?
To get all users that belong in both 1 and 2 departments:
SELECT *
FROM User
WHERE User.ID IN (SELECT UserID FROM UserDepartment WHERE IDDepartment = 1)
AND User.ID IN (SELECT UserID FROM UserDepartment WHERE IDDepartment = 2)
The results are essentially the same as the latter query you have, except it specifically looks for people who are in both "1" and "2", rather than anybody who has 2 or more department records that belong to "1" or "2" (doesn't include people with two "1" records but no "2" records).
If there's information from the userdepartment table you want included in the results, just change it to:
SELECT *
FROM User US
INNER JOIN (SELECT * FROM UserDepartment WHERE IDDepartment IN(1,2)) DP ON US.ID = DP.UserID
WHERE User.ID IN (SELECT UserID FROM UserDepartment WHERE IDDepartment = 1)
AND User.ID IN (SELECT UserID FROM UserDepartment WHERE IDDepartment = 2)
This seems like a job for INTERSECT. You can get the list of user id's you want by doing something like:
SELECT IDUser FROM UserDepartment WHERE IDDepartment = 1
INTERSECT
SELECT IDUser FROM UserDepartment WHERE IDDepartment = 2
/*** If only users for dept 1 and 2 need to be shown the use this query ****/
Select * from #User u
inner join
(
Select d1.UserID from #UserDepartment d1
where d1.Department = 1
intersect
Select d2.UserID from #UserDepartment d2
where d2.Department = 2
except
Select d2.UserID from #UserDepartment d2
where d2.Department not in (1,2)
)t on u.ID = t.UserID
/***If the users that belong to dept 1 and 2 and can exist in
other depts then use this one**/
Select * from #User u
inner join
(
Select d1.UserID from #UserDepartment d1
where d1.Department = 1
intersect
Select d2.UserID from #UserDepartment d2
where d2.Department = 2
)t on u.ID = t.UserID
/**To test use the following commented code**/
/*Select * into #User from
(
Select 1 as ID, 'A' as Name
union
Select 2,'B'
union
Select 3,'C'
union
Select 4,'D'
union
Select 5,'E'
union
Select 6,'F'
union
Select 7,'G'
union
Select 8,'H'
union
Select 9,'I'
union
Select 10,'G'
)t
Select * into #Department from
(
Select 1 as ID, 'D1' as Name
union
Select 2,'D2'
union
Select 3,'D3'
union
Select 4,'D4'
)t2
Select * into #UserDepartment from
(
Select 1 AS ID ,1 AS UserID ,1 AS Department
union
Select 2,1,2
union
Select 3,1,3
union
Select 4,2,1
union
Select 5,3,2
union
Select 6,4,1
union
Select 7,4,2
)t3
*/

Transposing Rows to Columns and having values flow down in TSQL

I am using TSQL.
If I have the following results:
And I want to transpose this to be like this:
How can I achieve this?
I have the following query:
WITH q AS (
SELECT *
FROM tableOne
WHERE ID = 1
UNION ALL
SELECT m.*
FROM tableOne m
JOIN ON m.ParentID = q.ID
)
SELECT *
FROM q
This gives me all of the items underneath the specified node including the specified node.
It could be easy for us to help you if you add scripting data and not an image. Note that tbl is the name of your table, it is called 3 times. Try this:
select
a.fieldValue company,
b.fieldValue department,
c.fieldValue Job
from tbl a
inner join tbl b on a.parentId is null and a.id=b.parentID
inner join tbl c on b.id= c.parentID
If it does not bring desired results please add data as text and let me know, I could modify the query
Another option (assuming this is NOT a jagged hierarchy).
This is a standard Recursive CTE, with a little twist in the final SELECT
Example
;with cteP as (
Select ID
,ParentID
,PathID = cast(FieldValue as varchar(max))
From YourTable
Where ParentID is Null
Union All
Select ID = r.ID
,ParentID = r.ParentID
,PathID = cast(p.PathID+'|||'+r.FieldValue as varchar(max))
From YourTable r
Join cteP p on r.ParentID = p.ID)
Select ID
,B.*
From cteP A
Cross Apply (
Select Company = xDim.value('/x[1]','varchar(max)')
,Department = xDim.value('/x[2]','varchar(max)')
,Job = xDim.value('/x[3]','varchar(max)')
From (Select Cast('<x>' + replace(PathID,'|||','</x><x>')+'</x>' as xml) as xDim) as X
) B
Where ID not in (Select Distinct ParentID from YourTable where ParentID is not null)
Order By PathID
Returns

Simplifying the SQL Server query

Below query is taking a lot of time to return results in two different databases. Is there any way to simplify this query?
WITH tblParent AS
(
SELECT *
FROM REFERENCES
WHERE referenced_id = 208593
UNION ALL
SELECT REFERENCES.*
FROM REFERENCES
JOIN tblParent ON REFERENCES.referenced_id = tblParent.entity_Id
)
SELECT DISTINCT(entity_Id)
FROM tblParent
WHERE entity_Id <> 208593 AND field_type = 'ChildField'
OPTION(MAXRECURSION 5)
This should simplify it:
WITH tblParent AS
(
SELECT entity_Id, 0 c
FROM [REFERENCES]
WHERE referenced_id = 208593
UNION ALL
SELECT r.entity_Id, 1
FROM [REFERENCES] r
JOIN tblParent
ON r.referenced_id = tblParent.entity_Id
)
SELECT DISTINCT entity_Id
FROM tblParent
WHERE c = 1
OPTION(MAXRECURSION 5)
By checking the c value, it becomes apparent that it is a child value. I am assuming that this text has field_type = 'ChildField' for all childs.
(REFERENCES is a reserved word and DISTINCT is not a function)
Since you're only interested in the child entityId's, then just select the fields you need in the recursive CTE.
WITH tblParent AS (
SELECT entity_Id, referenced_id as baseId
FROM [REFERENCES]
WHERE referenced_id = 208593
UNION ALL
SELECT t.entity_Id, cte.baseId
FROM tblParent cte
JOIN [REFERENCES] t
ON (t.referenced_id = cte.entity_Id
AND t.entity_Id <> cte.baseId -- to avoid a circular reference
)
WHERE t.field_type = 'ChildField'
)
SELECT DISTINCT entity_Id
FROM tblParent
WHERE entity_Id <> baseId
OPTION(MAXRECURSION 5)
And you might want to double-check if there's an index on referenced_id.

Error using CTE in SQLITE3

I use these codes to get level(depth) of my parent/child categories table. and the isLeaf attribute:
with cteCat as (
select
id, parent,
[cteLevel] = 1
from Categories
where 1=1 and parent=0
union all
select
c.id, c.parent,
[cteLevel] = cc.cteLevel+1
from Categories c
join cteCat cc
on c.parent = cc.id
where 1=1
and c.parent <> 0
)
select
*
, CASE WHEN EXISTS (SELECT * FROM Categories c2 WHERE c2.parent = c1.id) THEN 0 ELSE 1 END AS IsLeaf
, (SELECT COUNT(*) FROM Categories c2 WHERE c2.parent = c1.id) AS Leafs
from cteCat c1
order by c1.id
result is:
id parent cteLevel IsLeaf Leafs
1 0 1 0 2
....
it's OK in SQLSERVER. but when i execute at sqlite, I get Error:
Error while executing SQL query on database 'ado':
no such column: cc.cteLevel
Any help? thanks.
SQLite does not support variable assignments like this.
However, you should be able to do the same with standard SQL:
WITH cteCat AS (
SELECT id,
parent,
1 AS cteLevel
FROM ...
WHERE ...
UNION ALL
SELECT c.id,
c.parent,
cc.cteLevel + 1
FROM ...
WHERE ...
)
SELECT ...

SQL - UNION, priority on the first select statement when doing order by

I'm trying to print out the results from the "GermanDB" Database first, while also showing everything from the Boston DB that was not in the German database. Can this be done in one query?
My query (the bold part functions but does not order the way I want)
select * from (
SELECT DISTINCT a.ProductRef
FROM GERMANDB.dbo.LOCATIONS AS a INNER JOIN GERMANDB.dbo.ITEMS AS b ON a.ProductRef = b.ProductRef
WHERE b.ACTIVE=1
) ta
UNION select * from
SELECT DISTINCT c.ProductRef
FROM BOSTONDB.dbo.LOCATIONS AS c INNER JOIN BOSTONDB.dbo.ITEMS AS d ON c.ProductRef = d.ProductRef
WHERE c.ACTIVE=1 (c.ProductRef NOT IN
(SELECT ProductRef FROM GERMANDB.dbo.ITEMS where ACTIVE=1))
) tb
order by ta.ProductRef** , tb.productRef
Just add one field to signal the priority. Like this:
select *, 0 as Priority from (
SELECT DISTINCT a.ProductRef
FROM GERMANDB.dbo.LOCATIONS AS a INNER JOIN GERMANDB.dbo.ITEMS AS b ON a.ProductRef = b.ProductRef
WHERE b.ACTIVE=1
) ta
UNION select *, 1 as Priority from
SELECT DISTINCT c.ProductRef
FROM BOSTONDB.dbo.LOCATIONS AS c INNER JOIN BOSTONDB.dbo.ITEMS AS d ON c.ProductRef = d.ProductRef
WHERE c.ACTIVE=1 (c.ProductRef NOT IN
(SELECT ProductRef FROM GERMANDB.dbo.ITEMS where ACTIVE=1))
) tb
order by Priority, ProductRef