I have the following data in my table
ID NAME PARENTID STATUS
----------- ------------------------------ ----------- ------
1 Folder A 0 0
2 Folder B 1 0
3 Folder C 2 1
4 Folder D 1 0
5 Folder E 4 0
6 Folder G 5 0
7 Folder H 6 1
Above records are from table [#Temp].
[Name] - Folder Name
[ID] - Unique ID of folder in database (identity)
[ParentID] - Represents the parent of current folder.
Query to populate above data in the table:
DECLARE #Temp TABLE
(
[ID] INT IDENTITY(1, 1) ,
[NAME] VARCHAR(30) ,
[PARENTID] INT,
[STATUS] BIT
)
INSERT INTO #Temp
SELECT 'Folder A' ,
0, 0
UNION
SELECT 'Folder B' ,
1, 0
UNION
SELECT 'Folder C' ,
2, 1
UNION
SELECT 'Folder D' ,
1, 0
UNION
SELECT 'Folder E' ,
4, 0
UNION
SELECT 'Folder G' ,
5, 0
UNION
SELECT 'Folder H' ,
6, 1
I am having the following query to get records where status=1
SELECT *
FROM #Temp WHERE [STATUS]=1
which gives the following output
ID NAME PARENTID STATUS
----------- ------------------------------ ----------- ------
3 Folder C 2 1
7 Folder H 6 1
My goal is to fetch those records too which are the parents (till parentid=0) of the records came with the above query. i.e I want to get this output which contains the parents of Folder C and Folder H:
ID NAME PARENTID STATUS
----------- ------------------------------ ----------- ------
1 Folder A 0 0
2 Folder B 1 0
3 Folder C 2 1
4 Folder D 1 0
5 Folder E 4 0
6 Folder G 5 0
7 Folder H 6 1
DECLARE #Temp TABLE
(
[ID] INT IDENTITY(1, 1) ,
[NAME] VARCHAR(30) ,
[PARENTID] INT ,
[STATUS] BIT
)
INSERT INTO #Temp
SELECT 'Folder A' ,
0 ,
0
UNION
SELECT 'Folder B' ,
1 ,
0
UNION
SELECT 'Folder C' ,
2 ,
1
UNION
SELECT 'Folder D' ,
1 ,
0
UNION
SELECT 'Folder E' ,
4 ,
0
UNION
SELECT 'Folder G' ,
5 ,
1
UNION
SELECT 'Folder H' ,
6 ,
0
CREATE TABLE #TempTable
(
[RowIndex] INT IDENTITY(1, 1) ,
[ID] INT ,
[ParentID] INT
)
DECLARE #TempIds TABLE ( [ID] INT )
INSERT INTO #TempTable
( [ID] ,
[ParentID]
)
SELECT [ID] ,
[ParentID]
FROM #Temp
WHERE [STATUS] = 1
DECLARE #intFlag INT
DECLARE #Count INT
SET #intFlag = 1
SELECT #Count = COUNT(1)
FROM #Temp
WHILE ( #intFlag <= #Count )
BEGIN
PRINT #intFlag
DECLARE #ID INT
SELECT #ID = [ParentID]
FROM #TempTable
WHERE RowIndex = #intFlag
SET #intFlag = #intFlag + 1;
WITH CTE
AS ( SELECT ID ,
ParentID
FROM #Temp
WHERE ID = #ID
UNION ALL
SELECT MainTbl.ID ,
MainTbl.ParentID
FROM #Temp MainTbl
INNER JOIN cte ON MainTbl.ID = cte.ParentID
)
INSERT INTO #TempIds
SELECT ID
FROM CTE
END
;WITH cte
AS ( SELECT DISTINCT
ID
FROM #TempIds
)
SELECT T.*
FROM cte
INNER JOIN #Temp T ON [T].ID = cte.ID
UNION
SELECT T1.*
FROM #TempTable TempTable
INNER JOIN #Temp T1 ON [TempTable].ID = T1.ID
DROP TABLE #TempTable
Output:
ID NAME PARENTID STATUS
----------- ------------------------------ ----------- ------
1 Folder A 0 0
2 Folder B 1 0
3 Folder C 2 1
4 Folder D 1 0
5 Folder E 4 0
6 Folder G 5 1
I made a recursive CTE - I start with the bottom, any folder that does not have a child. From there, I pass up whether or not a '1' Status was encountered in the 'GOOD' column, and the distinct clears out the duplicates created by starting at the bottom.
;WITH CTE AS
(
SELECT ID, NAME, PARENTID, STATUS GOOD, STATUS
FROM #Temp T1
WHERE NOT EXISTS (SELECT * FROM #Temp T2 WHERE T2.PARENTID = T1.ID)
UNION ALL
SELECT T.ID, T.NAME, T.PARENTID, T.STATUS | C.GOOD GOOD, T.STATUS
FROM #Temp T
JOIN CTE C
ON C.PARENTID = T.ID
)
SELECT DISTINCT ID, NAME, PARENTID, STATUS FROM CTE
WHERE GOOD = 1
ORDER BY ID ASC
Related
is this possible, i have results like below and want to stuff or combine each id
Num id RMA
item1 1 0
item1 1 0
item1 1 0
item1 8 1
item1 8 1
item1 8 1
item1 1 0
item1 1 0
item1 1 0
item2 8 1
item2 8 1
item2 8 1
item2 8 1
item2 8 1
item2 8 1
item2 8 1
item2 1 0
i would like to get the results like below
item1 id1=3,id8=3,id1=3
item2 id8=7,id1=1
If it's SQL server:
You can use a GROUP BY clause:
SELECT Num
,id
,COUNT(RMA) AS Count_RMA
FROM Table
GROUP BY Num
,id
It will output a table that counts each RMA for each unique Num and id combinaison.
Like this:
Num id Count_RMA
item1 1 6
item1 8 3
item2 8 7
item2 1 1
It is not difficult, however, you will need to create some additional steps, see the code bellow:
DECLARE #items TABLE (Num varchar(10) , id INT, RMA int , _group int default(0) )
declare #gIndcator int = 0 , #old_id int = 0
insert #items (num,id,rma) select 'item1' , 1 , 0
insert #items (num,id,rma) select 'item1' , 1 , 0
insert #items (num,id,rma) select 'item1' , 1 , 0
insert #items (num,id,rma) select 'item1' , 8 , 1
insert #items (num,id,rma) select 'item1' , 8 , 1
insert #items (num,id,rma) select 'item1' , 8 , 1
insert #items (num,id,rma) select 'item1' , 1 , 0
insert #items (num,id,rma) select 'item1' , 1 , 0
insert #items (num,id,rma) select 'item1' , 1 , 0
insert #items (num,id,rma) select 'item2' , 8 , 1
insert #items (num,id,rma) select 'item2' , 8 , 1
insert #items (num,id,rma) select 'item2' , 8 , 1
insert #items (num,id,rma) select 'item2' , 8 , 1
insert #items (num,id,rma) select 'item2' , 8 , 1
insert #items (num,id,rma) select 'item2' , 8 , 1
insert #items (num,id,rma) select 'item2' , 8 , 1
insert #items (num,id,rma) select 'item2' , 1 , 0
UPDATE #items
SET #gIndcator = _group = case when #old_id <> id then #gIndcator + 1 else #gIndcator end
, #old_id = id
select * from #items
select distinct
num
, ids = stuff((
select ',id'+convert(varchar(12),b.id) + '=' +convert(varchar(12),b._count)
from (
SELECT num, id , _group, COUNT(1) _count
FROM #items c
where a.Num = c.Num
group by Num, id,_group) b
where a.Num = b.Num
order by b.Num,_group
for xml path (''), type).value('.','nvarchar(max)')
,1,1,'')
from #items a
item1 id1=3,id8=3,id1=3
item2 id8=7,id1=1
It looks something like this:
select t.*,
stuff( (select ',' + cast(id as varchar(max)) + '=' + cast(cnt as varchar(max))
from t tt
where t.num = tt.num
group by id
for xml path ('')
), 1, 1, ''
) as
from (select num from t) t
;WITH CTE(Num , id, RMA)
AS
(
SELECT 'item1', 1 , 0 UNION ALL
SELECT 'item1', 1 , 0 UNION ALL
SELECT 'item1', 1 , 0 UNION ALL
SELECT 'item1', 8 , 1 UNION ALL
SELECT 'item1', 8 , 1 UNION ALL
SELECT 'item1', 8 , 1 UNION ALL
SELECT 'item1', 1 , 0 UNION ALL
SELECT 'item1', 1 , 0 UNION ALL
SELECT 'item1', 1 , 0 UNION ALL
SELECT 'item2', 8 , 1 UNION ALL
SELECT 'item2', 8 , 1 UNION ALL
SELECT 'item2', 8 , 1 UNION ALL
SELECT 'item2', 8 , 1 UNION ALL
SELECT 'item2', 8 , 1 UNION ALL
SELECT 'item2', 8 , 1 UNION ALL
SELECT 'item2', 8 , 1 UNION ALL
SELECT 'item2', 1 , 0
)
,Final
AS
(
SELECT Num,idval,cnt AS COUNT_RMA From
(
SELECT NUM,' id'+CASt(id AS Varchar(2))AS idval,cnt, ROW_NUMBER()OVEr(Partition by NUm,Cnt order by Num)AS Seq
From
(
SELECT NUm,id,COUNT(Id)Over(Partition by Num ,id order by Num)Cnt from CTE
)Dt
)Fnl
where Fnl.Seq=1
)
SELECT DISTINCT num, STUFF((SELECT DISTINCT ', '+CAST(''+CAST(idval AS VARCHAR)+'='+CAST(COUNT_RMA AS VARCHAR) AS VARCHAR(100))
From Final I WHERE O.Num=I.Num foR XML PATH('')),1,1,'') As Requiredvalue
from Final O
I have a table that looks like this:
ID SuppressionTypeID PersonID
------------------------------
1 1 123
2 1 456
3 2 456
I want to get a rolling count (distinct people) rather than a normal group by count.
e.g. not this:
SuppressionTypeID Count
---------------------------
1 2
2 1
This:
SuppressionTypeID RecordsLost
----------------------------------
1 2
2 0
The latter being zero as we lost person 456 on suppresiontypeid 1.
Thanks in advance.
You may need to use a temporary table or a table variable as shown below
DECLARE #t TABLE (
ID INT
,SuppressionTypeID INT
,PersonID INT
)
INSERT INTO #t
SELECT 1
,1
,123
UNION ALL
SELECT 2
,1
,456
UNION ALL
SELECT 3
,2
,456
DECLARE #t1 TABLE (
ID INT
,SuppressionTypeID INT
,PersonID INT
,firstid INT
)
INSERT INTO #t1
SELECT *
,NULL
FROM #t
UPDATE t1
SET t1.firstid = t2.firstid
FROM #t1 AS t1
INNER JOIN (
SELECT personid
,min(SuppressionTypeID) AS firstid
FROM #t1
GROUP BY personid
) AS t2 ON t1.PersonID = t2.PersonID
SELECT coalesce(t2.firstid, t1.SuppressionTypeID) AS SuppressionTypeID
,count(DISTINCT t2.personid) AS count
FROM #t1 AS t1
LEFT JOIN #t1 AS t2 ON t1.personid = t2.personid
AND t1.SuppressionTypeID = t2.firstid
GROUP BY coalesce(t2.firstid, t1.SuppressionTypeID)
The result is
SuppressionTypeID count
----------------- -----------
1 2
2 0
You can try;
with tmp_tbl as (
select
x.SuppressionTypeID, count(x.PersonID) as RecordsLost
from (
select
min(SuppressionTypeID) as SuppressionTypeID,
PersonID
from tbl
group by PersonID
) as x
group by x.PersonID
order by x.SuppressionTypeID
)
select
distict t.SuppressionTypeID, coalesce(tmp.RecordsLost, 0) as RecordsLost
from tbl t
left join tmp_tbl tmp on tmp.SuppressionTypeID = t.SuppressionTypeID
I have a CTE-query that displays a tree using recursion. This works great when displaying the whole tree. But I want to pass in ID as a variable and include the siblings for each current node.
Testcode:
DECLARE #TT TABLE
(
ID int,
Name varchar(25),
ParentID int,
SortIndex int
)
INSERT #TT
SELECT 1, 'A', NULL, 1 UNION ALL
SELECT 2, 'B_1', 3, 1 UNION ALL
SELECT 3, 'B', 1, 2 UNION ALL
SELECT 4, 'B_2', 3, 2 UNION ALL
SELECT 5, 'C', 1, 3 UNION ALL
SELECT 6, 'C_2', 5, 2 UNION ALL
SELECT 7, 'A_1', 1, 1 UNION ALL
SELECT 8, 'A_2', 1, 2 UNION ALL
SELECT 9, 'C_1', 5, 1
;WITH CTETree
AS
(
SELECT *, CAST(NULL AS VARCHAR(25)) AS ParentName, 1 AS Lev,
CAST(ROW_NUMBER() OVER(ORDER BY SortIndex) AS VARBINARY(MAX)) AS SortPath
FROM #TT
WHERE ParentID IS NULL
UNION ALL
SELECT F.*, CTETree.Name AS ParentName, Lev + 1,
SortPath + CAST(ROW_NUMBER() OVER(ORDER BY F.SortIndex) AS BINARY(32))
FROM #TT AS F
INNER JOIN CTETree
ON F.ParentID = CTETree.ID
)
SELECT * FROM CTETree
ORDER BY SortPath
/*
DESIRED RESULT:
WHEN ID = 3 PASSED IN:
1 A NULL 1 NULL 1
3 B 1 2 A 2
2 B_1 3 1 B 3
4 B_2 3 2 B 3
5 C 1 3 A 2
WHEN ID = 1 PASSED IN:
1 A NULL 1 NULL 1
3 B 1 2 A 2
5 C 1 3 A 2
WHEN ID = 9 PASSED IN:
1 A NULL 1 NULL 1
3 B 1 2 A 2
5 C 1 3 A 2
9 C_1 5 1 C 3
6 C_2 5 2 C 3
*/
SQL-Fiddle: http://sqlfiddle.com/#!3/d41d8/5526
Only in once I confront a question where disappear A_1 and A_2. Therefore add in exception(in case clause).
If you need this records delete this expression AND f.ID != 7 AND f.ID != 8.
DECLARE #ID int = 3 -- variable wich you want pass
DECLARE #Matched int = (SELECT CASE WHEN ParentID = 1 THEN ID ELSE ParentID END FROM #TT WHERE ID = #ID)
;WITH CTETree
AS
(
SELECT *, CAST(NULL AS VARCHAR(25)) AS ParentName, 1 AS Lev,
CAST(ROW_NUMBER() OVER(ORDER BY SortIndex) AS VARBINARY(MAX)) AS SortPath,
0 AS Matched
FROM #TT
WHERE ParentID IS NULL
UNION ALL
SELECT F.*, cte.Name AS ParentName, cte.Lev + 1,
SortPath + CAST(ROW_NUMBER() OVER(ORDER BY F.SortIndex) AS BINARY(32)),
CASE WHEN (cte.ID = 1 AND f.ID != 7 AND f.ID != 8)
OR (cte.ID = #Matched AND cte.Lev + 1 > 2)
THEN 1 END AS Matched
FROM #TT AS F INNER JOIN CTETree cte
ON F.ParentID = cte.ID
)
SELECT ID, Name, ParentID, SortIndex, ParentName, Lev FROM CTETree
WHERE Matched IS NOT NULL
ORDER BY SortPath
Demo on SQLFiddle
Let's say I have a database schema like this:
RowId ParentId Name
------ ---------- ------
1 NULL Level1
2 NULL Level2
3 1 Leaf1
4 1 Leaf2
5 2 Leaf1
6 3 LeafX
Basically, the tree would look as such:
Level1
Leaf1
LeafX
Leaf2
Level2
Leaf1
I need to extract all ancestor LEVEL of LeafX in the most efficient and dynamic way.
So it will output: Leaf1, Leaf2, and Leaf1 (of Level2)
How do I do this in T-SQL? Thanks
This will give you the result you want.
;with C as
(
select T.rowid,
T.parentid,
T.name,
1 as Lvl
from YourTable as T
where T.parentid is null
union all
select T.rowid,
T.parentid,
T.name,
C.Lvl + 1
from YourTable as T
inner join C
on T.parentid = C.rowid
)
select *
from C
where C.Lvl = (
select C.lvl-1
from C
where C.name = 'LeafX'
)
Update
And this might be faster for you. You have to test on your data.
declare #Level int;
with C as
(
select T.rowid,
T.parentid
from #t as T
where T.name = 'LeafX'
union all
select T.rowid,
T.parentid
from #t as T
inner join C
on T.rowid = C.parentid
)
select #Level = count(*) - 1
from C;
with C as
(
select T.rowid,
T.parentid,
T.name,
1 as Lvl
from #t as T
where T.parentid is null
union all
select T.rowid,
T.parentid,
T.name,
C.Lvl + 1
from #t as T
inner join C
on T.parentid = C.rowid
where C.Lvl < #Level
)
select *
from C
where C.Lvl = #Level;
There's a few methods to do that. My favourite is to create special table Trees_Parents, where you will store every parent for evere node.
So if have structure like that
RowId ParentId Name
------ ---------- ------
1 NULL Level1
2 NULL Level2
3 1 Leaf1
4 1 Leaf2
5 2 Leaf1
6 3 LeafX
your Trees_Parents table will looks like
RowId ParentId
------ ----------
1 1
2 2
3 3
3 1
4 4
4 1
5 5
5 2
6 6
6 1
6 3
then when you need to retrieve all children you just write
select RowID from Trees_Parents where ParentId = 1
I'm storing row self in this table to avoid unions, if you don't need it you can write
select RowID from Trees_Parents where ParentId = 1 and ParentId <> RowId
And for all parents you'll write
select ParentId from Trees_Parents where RowId = 6 and ParentId <> RowId
You can also store Table_Name in table Trees_Parents so you can use it for different tables
Another way is to write recursive WITH clause, but if your tree is big and it's not changing frequently I think it's better to store parents data in additional table
Well you can use recursive solution. You need to get all nodes with Depth = Depth of your node - 1
declare #Temp table (RowId int, ParentId int, Name nvarchar(128))
insert into #Temp
select 1, null, 'Level1' union all
select 2, null, 'Level2' union all
select 3, 1, 'Leaf1' union all
select 4, 1, 'Leaf2' union all
select 5, 2, 'Leaf3' union all
select 6, 3, 'LeafX';
with Parents
as
(
select T.RowId, 0 as Depth from #Temp as T where T.ParentId is null
union all
select T.RowId, P.Depth + 1
from Parents as P
inner join #Temp as T on T.ParentId = P.RowId
)
select T.Name
from Parents as P
outer apply (select TT.Depth from Parents as TT where TT.RowId = 6) as CALC
left outer join #Temp as T on T.RowId = P.RowId
where P.Depth = CALC.Depth - 1
declare #t table(rowid int, parentid int, name varchar(10))
insert #t values(1,NULL,'Level1')
insert #t values(2,NULL,'Level2')
insert #t values(3,1,'Leaf1')
insert #t values(4,1,'Leaf2')
insert #t values(5,2,'Leaf1')
insert #t values(6,3,'LeafX')
;with a as
(
select rowid, parentid, 0 level from #t where name = 'leafx'
union all
select t.rowid, t.parentid, level + 1 from #t t
join a on a.parentid = t.rowid
), b as
(
select rowid, parentid,name, 0 level from #t where parentid is null
union all
select t.rowid, t.parentid,t.name, level + 1
from b join #t t on b.rowid = t.parentid
)
select rowid, parentid, name from b
where level = (select max(level)-1 from a)
rowid parentid name
5 2 Leaf1
3 1 Leaf1
4 1 Leaf2
Input:
ID groupId RowID Data
1 1 1 W
2 1 1 NULL
3 1 1 NULL
4 1 1 Z
5 1 2 NULL
6 1 2 NULL
7 1 2 X
8 1 2 NULL
9 1 3 NULL
10 1 3 NULL
11 1 3 Y
12 1 3 NULL
Expected Output
GroupId NewData
1 2Y1,2X1,W2Z
For every Null there will be a numeric count. That is if there are two nulls then the numeric value will be 2.
The ddl is as under
DECLARE #t TABLE(ID INT IDENTITY(1,1) , GroupId INT, RowID INT, Data VARCHAR(10))
INSERT INTO #t (GroupId, RowID,DATA)
SELECT 1,1,'W' UNION ALL SELECT 1,1,NULL UNION ALL SELECT 1,1,NULL UNION ALL SELECT 1,1,'Z' UNION ALL SELECT 1,2,NULL UNION ALL
SELECT 1,2,NULL UNION ALL SELECT 1,2,'X' UNION ALL SELECT 1,2,NULL UNION ALL SELECT 1,3,NULL UNION ALL SELECT 1,3,NULL UNION ALL
SELECT 1,3,'Y' UNION ALL SELECT 1,3,NULL
select * from #t
My version is as under but not the correct output
;with t as (
select GroupID, id, RowID, convert(varchar(25), case when Data is null then '' else Data end) Val,
case when Data is null then 1 else 0 end NullCount from #t where id = 1
union all
select t.GroupID, a.id,a.RowID, convert(varchar(25), Val +
case when Data is not null or (t.RowID <> a.RowID and NullCount > 0) then ltrim(NullCount) else '' end +
case when t.RowID <> a.RowID then ',' else '' end + isnull(Data, '')),
case when Data is null then NullCount + 1 else 0 end NullCount
from t inner join #t a on t.GroupID = a.GroupID and t.id + 1 = a.id
)
select GroupID, Data = Val + case when NullCount > 0 then ltrim(NullCount) else '' end from t
where id = (select max(id) from #t where GroupID = t.GroupId)
Is yielding the below output
GroupID Data
1 W2Z,2X1,3Y1
Please help me out
Thanks in advance
Kind of messy and most likely can be improved
;With RawData AS
(
select * from #t
)
,Ranked1 as
(
select *, RANK() OVER (PARTITION BY GroupId, RowID ORDER BY ID, GroupId, RowID) R from #t
)
,Ranked2 as
(
select *, R - RANK() OVER (PARTITION BY GroupId, RowID ORDER BY ID, GroupId, RowID) R2 from Ranked1
where Data is null
)
,Ranked3 as
(
select MIN(ID) as MinID, GroupId, RowID, R2, COUNT(*) C2 from Ranked2
group by GroupId, RowID, R2
)
,Ranked4 as
(
select RD.ID, RD.GroupId, RD.RowID, ISNULL(Data, C2) as C3 from RawData RD
left join Ranked3 R3 on RD.ID = R3.MinID and RD.GroupId = R3.GroupId and RD.RowID = R3.RowID
where ISNULL(Data, C2) is not null
)
,Grouped as
(
select GroupId, RowID,
(
select isnull(C3, '') from Ranked4 as R41
where R41.GroupId = R42.GroupId and R41.RowID = R42.RowID
order by GroupId, RowID for xml path('')
) as C4
from Ranked4 as R42
group by GroupId, RowID
)
select GroupId,
stuff((
select ',' + C4 from Grouped as G1
where G1.GroupId = G2.GroupId
order by GroupId for xml path('')
), 1, 1, '')
from Grouped G2
group by GroupId