Transposing Rows to Columns and having values flow down in TSQL - sql

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

Related

SQL Select top parentId on a list of Id's and return row if all same top parentId

I have to script i would like to combine in to one:
SELECT [CategoryId] FROM [dbo].[Unit] where Id in (716,724)
and
DECLARE #Id INT = 15
;WITH cteGetRootID
As
(
SELECT
Id, [Name], ParentId, 1 AS CodePosition
FROM
Category WHERE Id = #Id
UNION All
SELECT
ic.Id, ic.[Name], ic.ParentId, CodePosition + 1
FROM Category ic
INNER JOIN cteGetRootID cte ON ic.Id = cte.ParentId
)
SELECT top 1 Id, [Name] FROM cteGetRootID
ORDER BY CodePosition desc
I would like to do low code for all result in first part and return Root Catogry if all have same Root
Like DECLARE #Id INT = SELECT [CategoryId] FROM [dbo].[Unit] where Id in (716,724)
Hope it make sence
You can use a join between dbo.Unit and dbo.Category in your first select of the union:
;WITH cteGetRootID
As
(
SELECT
c.Id, c.[Name], c.ParentId, 1 AS CodePosition
FROM
Category c
INNER JOIN [dbo].[Unit] u on u.CategoryID = c.Id
WHERE u.Id in (716,724)
UNION All
SELECT
ic.Id, ic.[Name], ic.ParentId, CodePosition + 1
FROM Category ic
INNER JOIN cteGetRootID cte ON ic.Id = cte.ParentId
)
SELECT top 1 Id, [Name] FROM cteGetRootID
ORDER BY CodePosition desc

How to add a temp table in SQL server

I am trying to add a temp table to my query so that I can query that temp table, I have searched the internet but I couldn't get a solution.
this is my query
;WITH cte AS (
SELECT ID, g.Name
FROM game.Game g WITH(NOLOCK
WHERE ID IN (SELECT Data FROM system.Split(1, ','))
UNION ALL
SELECT g.ID, g.Name
FROM game.Game g WITH(NOLOCK)
JOIN cte ON g.ParentID = cte.ID
)
SELECT c.ID,
c.Name
FROM cte c
INNER JOIN list.Type gt WITH(NOLOCK) ON c.TypeId = gt.TypeID
WHERE c.ID NOT IN (SELECT Data FROM system.Split(1, ','))
AND c.ID IN (SELECT ID FROM game.code WITH(NOLOCK)
WHERE ID = c.ID
AND StatusCode IN ('OP', 'CL', 'SU')
AND isDisplay = 'True'
AND GETDATE() BETWEEN DisplayStart AND DisplayEnd
AND GETDATE() < ISNULL(ResultDateTime, ResultExpected)
)
which gives me the following when I run it
ID | Name
1111 | BaseBall
2222 |BasketBall
45896 |Relay
now I tried to create a temp table as follows
Create Table #temp(
ID int,
Name varchar
)
;WITH cte AS (
SELECT ID, g.Name
FROM game.Game g WITH(NOLOCK)
WHERE ID IN (SELECT Data FROM system.Split(1, ','))
UNION ALL
SELECT g.ID, g.Name
FROM game.Game g WITH(NOLOCK)
JOIN cte ON g.ParentID = cte.ID
)
insert into #temp // i wanted to set these values in the temp table
SELECT c.ID,
c.Name
FROM cte c
INNER JOIN list.Type gt WITH(NOLOCK) ON c.TypeId = gt.TypeID
WHERE c.ID NOT IN (SELECT Data FROM system.Split(1, ','))
AND c.ID IN (SELECT ID FROM game.code WITH(NOLOCK)
WHERE ID = c.ID
AND StatusCode IN ('OP', 'CL', 'SU')
AND isDisplay = 'True'
AND GETDATE() BETWEEN DisplayStart AND DisplayEnd
AND GETDATE() < ISNULL(ResultDateTime, ResultExpected)
)
every time I try to store this information in the temp table it gives me an error 'Column name or number of supplied values does not match table definition.' But I only have two values in. What am I doing wrong that I cant see?
First, why not just use select into?
IF OBJECT_ID('TempDB..#temp') IS NOT NULL
BEGIN
DROP TABLE #temp
END
select c.ID, c.Name
into #temp
from . . .
Then you don't need to define #temp as a table.
Next, your definition is bad, because Name has only one character. This would be fixed with select into.
However, I don't know why you are getting the particular error you are getting. The numbers of columns appears to match.

why are the results of the two queries different

the first query:
SELECT u.id , prop1.id
FROM ( SELECT '9fbc6e9b59504c08ac643752c1e2d033' AS id ,
'|6813dbbfec6241a19b8d2316d2cb2a65,1|' AS customprop
UNION
SELECT 'f2271c45682f45fc84527c4afff0ab16' AS id ,
'|6813dbbfec6241a19b8d2316d2cb2a65,2|' AS customprop
) u
INNER JOIN ( SELECT ROW_NUMBER() OVER ( ORDER BY a.Id ) id ,
A.Id propId ,
B.NAME
FROM ( SELECT '6813dbbfec6241a19b8d2316d2cb2a65' AS id ,
CONVERT(XML, '<v>1,职业资格1</v><v>2,职业资格2</v>') AS value
) A
OUTER APPLY ( SELECT Name = N.v.value('.',
'nvarchar(Max)')
FROM A.[VALUE].nodes('/v') N ( v )
) B
) prop1 ON CHARINDEX('|' + prop1.propid + ','
+ CONVERT(NVARCHAR(10), prop1.id)
+ '|', u.customprop) > 0
GROUP BY u.id ,
prop1.id
the second query:
SELECT u.id ,prop1.id, count(*)
FROM ( SELECT '9fbc6e9b59504c08ac643752c1e2d033' AS id ,
'|6813dbbfec6241a19b8d2316d2cb2a65,1|' AS customprop
UNION
SELECT 'f2271c45682f45fc84527c4afff0ab16' AS id ,
'|6813dbbfec6241a19b8d2316d2cb2a65,2|' AS customprop
) u
INNER JOIN ( SELECT ROW_NUMBER() OVER ( ORDER BY a.Id ) id ,
A.Id propId ,
B.NAME
FROM ( SELECT '6813dbbfec6241a19b8d2316d2cb2a65' AS id ,
CONVERT(XML, '<v>1,职业资格1</v><v>2,职业资格2</v>') AS value
) A
OUTER APPLY ( SELECT Name = N.v.value('.',
'nvarchar(Max)')
FROM A.[VALUE].nodes('/v') N ( v )
) B
) prop1 ON CHARINDEX('|' + prop1.propid + ','
+ CONVERT(NVARCHAR(10), prop1.id)
+ '|', u.customprop) > 0
GROUP BY u.id ,
prop1.id
sql can be executed on sqlserver 2005 directly.
the first query can produce one item and the second query produce two items.
I think that the two queries should both produce two items.
I have thouht for three days and I really want to konw why.
I'm a Chinese and my English is poor.I hope you can understand my description
Tough question, but the problem is with this line:
INNER JOIN ( SELECT ROW_NUMBER() OVER ( ORDER BY a.Id ) id ,
The ORDER BY is ambiguous and consequently, if it is executed multiple times (which it can be because of the INNER JOIN it is contained in), it may not always return the same ordering/assignment. This can cause a latter join condition to only match on one record instead of two, which is what happens in the query plan being used for the version without the count(*) column.
To fix this, you just need to add something to make the ordering assignment unique, like this:
INNER JOIN ( SELECT ROW_NUMBER() OVER ( ORDER BY a.Id, B.Name ASC ) id ,
Try it like this, it should work.
Your problem is with the ORDER BY clause of the ROW_NUMBER - since the a.ID is not unique the outcome is unpredictable. Make that unique and your problem will go away - you can use something like
...SELECT ROW_NUMBER() OVER ( ORDER BY newid() ) id...

Finding Top level parent of each row of a table [SQL Server 2008]

I have following two tables
Table Person
Id Name
1 A
2 B
3 C
4 D
5 E
Table RelationHierarchy
ParentId CHildId
2 1
3 2
4 3
This will form a tree like structure
D
|
C
|
B
|
A
ParentId and ChildId are foreign keys of Id column of Person Table
I need to write SQL that Can fetch me Top Level Parent i-e Root of Each Person.
Following CTE can do this for Each. I converted that to a Function and ran it for each row of Person. I have got about 3k rows in Person table and it takes about 10 Secs to do that. Can anyone suggest a approach that can take less. The Problem is the function that runs following CTE runs 3k times
DECLARE #childID INT
SET #childID = 1 --chield to search
;WITH RCTE AS
(
SELECT *, 1 AS Lvl FROM RelationHierarchy
WHERE ChildID = #childID
UNION ALL
SELECT rh.*, Lvl+1 AS Lvl FROM dbo.RelationHierarchy rh
INNER JOIN RCTE rc ON rh.CHildId = rc.ParentId
)
SELECT TOP 1 id, Name
FROM RCTE r
inner JOIN dbo.Person p ON p.id = r.ParentId
ORDER BY lvl DESC
I have also updated the answer in the original question, but never-mind, here is a copy also:
;WITH RCTE AS
(
SELECT ParentId, ChildId, 1 AS Lvl FROM RelationHierarchy
UNION ALL
SELECT rh.ParentId, rc.ChildId, Lvl+1 AS Lvl
FROM dbo.RelationHierarchy rh
INNER JOIN RCTE rc ON rh.ChildId = rc.ParentId
)
,CTE_RN AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY r.ChildID ORDER BY r.Lvl DESC) RN
FROM RCTE r
)
SELECT pc.Id AS ChildID, pc.Name AS ChildName, r.ParentId, pp.Name AS ParentName
FROM dbo.Person pc
LEFT JOIN CTE_RN r ON pc.id = r.CHildId AND RN =1
LEFT JOIN dbo.Person pp ON pp.id = r.ParentId
SQLFiddle DEMO
Note that the slight difference is in recursive part of CTE. ChildID is now rewritten each time from the anchor part. Also addition is ROW_NUMBER() function (and new CTE) to get the top level for each child at the end.
EDIT - Version2
After finding a performance issues with first query, here is an improved version. Going from top-to-bottom, instead of other way around - eliminating creating of extra rows in CTE, should be much faster on high number of recursions:
;WITH RCTE AS
(
SELECT ParentId, CHildId, 1 AS Lvl FROM RelationHierarchy r1
WHERE NOT EXISTS (SELECT * FROM RelationHierarchy r2 WHERE r2.CHildId = r1.ParentId)
UNION ALL
SELECT rc.ParentId, rh.CHildId, Lvl+1 AS Lvl
FROM dbo.RelationHierarchy rh
INNER JOIN RCTE rc ON rc.CHildId = rh.ParentId
)
SELECT pc.Id AS ChildID, pc.Name AS ChildName, r.ParentId, pp.Name AS ParentName
FROM dbo.Person pc
LEFT JOIN RCTE r ON pc.id = r.CHildId
LEFT JOIN dbo.Person pp ON pp.id = r.ParentId
SQLFiddle DEMO
You could try to use a loop. As you will get many levels of recursion with your approach:
declare #child int = 0
declare #parent int = 1 --child to search
while #child <> #parent
BEGIN
set #child = #parent
select #parent = Parentid from #parentchild where ChildID = #child
END
select #parent
Another way to do this in a loop if you wanted to work with sets is:
SELECT *
INTO #parentchild
from RelationHierarchy
WHILE EXISTS
(select 1 from #parentchild A inner join #parentchild B on A.ChildID = B.ParentID Where A.ParentID <> B.ParentID )
BEGIN
update B set B.ParentID = A.ParentID
from #parentchild A inner join #parentchild B on A.ChildID = B.ParentID
END
select * from #parentchild

Finding a Top Level Parent in SQL

I have got two tables as following
Table Person
Id Name
1 A
2 B
3 C
4 D
5 E
Table RelationHierarchy
ParentId ChildId
2 1
3 2
4 3
This will form a tree like structure
D
|
C
|
B
|
A
ParentId and ChildId are foreign keys of Id column of Person Table
I need to write SQL that Can fetch me Top Level Parent i-e Root. Can anyone suggest any SQL that can help me accomplish this
You can use recursive CTE to achieve that:
DECLARE #childID INT
SET #childID = 1 --chield to search
;WITH RCTE AS
(
SELECT *, 1 AS Lvl FROM RelationHierarchy
WHERE ChildID = #childID
UNION ALL
SELECT rh.*, Lvl+1 AS Lvl FROM dbo.RelationHierarchy rh
INNER JOIN RCTE rc ON rh.CHildId = rc.ParentId
)
SELECT TOP 1 id, Name
FROM RCTE r
inner JOIN dbo.Person p ON p.id = r.ParentId
ORDER BY lvl DESC
SQLFiddle DEMO
EDIT - for updated request for top level parents for all children:
;WITH RCTE AS
(
SELECT ParentId, ChildId, 1 AS Lvl FROM RelationHierarchy
UNION ALL
SELECT rh.ParentId, rc.ChildId, Lvl+1 AS Lvl
FROM dbo.RelationHierarchy rh
INNER JOIN RCTE rc ON rh.ChildId = rc.ParentId
)
,CTE_RN AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY r.ChildID ORDER BY r.Lvl DESC) RN
FROM RCTE r
)
SELECT r.ChildId, pc.Name AS ChildName, r.ParentId, pp.Name AS ParentName
FROM CTE_RN r
INNER JOIN dbo.Person pp ON pp.id = r.ParentId
INNER JOIN dbo.Person pc ON pc.id = r.ChildId
WHERE RN =1
SQLFiddle DEMO
EDIT2 - to get all persons change JOINS a bit at the end:
SELECT pc.Id AS ChildID, pc.Name AS ChildName, r.ParentId, pp.Name AS ParentName
FROM dbo.Person pc
LEFT JOIN CTE_RN r ON pc.id = r.CHildId AND RN =1
LEFT JOIN dbo.Person pp ON pp.id = r.ParentId
SQLFiddle DEMo
I've used this pattern to associate items in a hierarchy with the item's root node.
Essentially recursing the hierarchies maintaining the values of the root node as additional columns appended to each row. Hope this helps.
with allRows as (
select ItemId, ItemName, ItemId [RootId],ItemName [RootName]
from parentChildTable
where ParentItemId is null
union all
select a1.ItemId,a1.ItemName,a2.[RootId],a2.[RootName]
from parentChildTable a1
join allRows a2 on a2.ItemId = a1.ParentItemId
)
select * from allRows
To find all top-level parents, use a query like:
select p.Name
from Person p
where not exists
(select null
from RelationHierarchy r
where r.ChildId = p.Id)
SQLFiddle here.
To find the top-level parent of a specific child, use:
with cte as
(select t.ParentId TopParent, t.ChildId
from RelationHierarchy t
left join RelationHierarchy p on p.ChildId = t.ParentId
where p.ChildId is null
union all
select t.TopParent TopParent, c.ChildId
from cte t
join RelationHierarchy c on t.ChildId = c.ParentId)
select p.name
from cte h
join Person p on h.TopParent = p.Id
where h.ChildId=3 /*or whichever child is required*/
SQLFiddle here.
Try this.
The recursive CTE will find the person and walk up the hierarchy until it finds no parent.
-- This CTE will find the ancestors along with a measure of how far up
-- the hierarchy each ancestor is from the selected person.
with ancestor as (
select ParentId as AncestorId, 0 as distance
from RelationHierarchy
where CHildId = ?
union all
select h.ParentId, a.distance + 1
from ancestor a inner join RelationHierarchy rh on a.AncestorId = rh.ChildId
)
select AncestorId
from ancestor
where distance = (select max(distance) from ancestor)
Something like this will work for above example:
SELECT ParentId FROM RelationHierarchy
WHERE ParentId NOT IN (SELECT CHildId FROM RelationHierarchy)
The only way you can do this in "standard" SQL is to assume a maximum depth for the tree, and then do joins for each level. The following gets the top level id:
select rh1.ChildId,
coalesce(rh4.parentid, rh3.parentid, rh2.parentid, rh1.parentid) as topLevel
from RelationshipHierarchy rh1 left outer join
RelationshipHierarchy rh2
on rh1.parentId = rh2.childId left outer join
RelationshipHierarchy rh3
on rh2.parentId = rh3.childId left outer join
RelationshipHierarchy rh4
on rh3.parentId = rh4.childId;
If you want the name, you can just join it in:
select rh1.ChildId,
coalesce(rh4.parentid, rh3.parentid, rh2.parentid, rh1.parentid) as topLevel,
p.name
from RelationshipHierarchy rh1 left outer join
RelationshipHierarchy rh2
on rh1.parentId = rh2.childId left outer join
RelationshipHierarchy rh3
on rh2.parentId = rh3.childId left outer join
RelationshipHierarchy rh4
on rh3.parentId = rh4.childId left outer join
Person p
on p.id = coalesce(rh4.parentid, rh3.parentid, rh2.parentid, rh1.parentid);
Get all top parents using path
path format: rootId/.../parentId/nodeId/
select t1.path from nodes t1 inner join nodes t2
on t1.path like t2.path+'%'
group by t1.path
having len(t1.path)-len(replace(t1.path, '/', ''))
=min(len(t2.path)-len(replace(t2.path, '/', '')))
Give this a go:
select id,name
from person p
where not exists
(
select 1
from relationhierarchy r
where r.childid= p.id
)
and exists
(
select 1
from relationhierarchy r
where r.parentid= p.id
)
It is not enough to just see if a child id exists as in your example E is present in the person table but not in the relationshiphierarchy table.
WITH CTE_MyTable AS (
SELECT Id, ParentId, NULL As RootParent, 1 As Lvl
FROM dbo.Ministry
UNION ALL
SELECT a.id, b.ParentId, a.ParentId As RootParent, Lvl + 1
FROM CTE_MyTableAS a INNER JOIN
dbo.MyTableAS b ON a.ParentId = b.Id
)
, CTE_Ministry_RN AS (
SELECT Id, RootParent, ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Lvl DESC) RN
FROM CTE_Ministry
)
SELECT Id, ISNULL(RootParent, Id) As RootParent
FROM CTE_Ministry_RN
WHERE RN = 1