How to find the count of children in each row?
For example:
1 ROW COUNT=1
2 ROW COUNT=0
...
and so on. In the next column
You can do this using recursive CTE, but it should be done with simple join. First, find the count of each node, excluding node without parents:
SELECT [ParentID]
,COUNT(*)
FROM MyTable
WHERE [ParentID] <> 0
GROUP BY [ParentID];
If this is OK, just join to the initial table:
SELECT *
FROM MyTable T1
LEFT JOIN
(
SELECT [ParentID]
,COUNT(*) AS [all_childs]
FROM MyTable
WHERE [ParentID] <> 0
GROUP BY [ParentID]
) T2
oN T1.[parentID] = T2.[ParentID];
This should be okay, you need isnull() function when ROW COUNT=0
SELECT
PA.ID,
PA.Title_Name,
ISNULL(P.COUNTT,0) CountOfID
FROM #My_Table PA
LEFT JOIN (
SELECT COUNT(*) COUNTT, Parent_ID from #My_Table GROUP BY Parent_ID
) as P on P.Parent_ID = PA.ID
Related
I am attempting to randomly join the rows of two tables (TableA and TableB) such that each row in TableA is joined to only one row in TableB and every row in TableB is joined to at least one row in TableA.
For example, a random join of TableA with 5 distinct rows and TableB with 3 distinct rows should result in something like this:
TableA TableB
1 3
2 1
3 1
4 2
5 1
However, sometimes not all the rows from TableB are included in the final result; so in the example above might have row 2 from TableB missing because in its place is either row 1 or 3 joined to row 4 on TableA. You can see this occur by executing the script a number of times and checking the result. It seems that it is necessary for some reason to use an interim table (#Q) to be able to ensure that a correct result is returned which has all rows from both TableA and TableB.
Can someone please explain why this is happening?
Also, can someone please advise on what would be a better way to get the desired result?
I understand that sometimes no result is returned due to a failure of some kind in the cross apply and ordering which i have yet to identify and goes to the point that I am sure there is a better way to perform this operation. I hope that makes sense. Thanks in advance!
declare #TableA table (
ID int
);
declare #TableB table (
ID int
);
declare #Q table (
RN int,
TableAID int,
TableBID int
);
with cte as (
select
1 as ID
union all
select
ID + 1
from cte
where ID < 5
)
insert #TableA (ID)
select ID from cte;
with cte as (
select
1 as ID
union all
select
ID + 1
from cte
where ID < 3
)
insert #TableB (ID)
select ID from cte;
select * from #TableA;
select * from #TableB;
with cte as (
select
row_number() over (partition by TableAID order by newid()) as RN,
TableAID,
TableBID
from (
select
a.ID as TableAID,
b.ID as TableBID
from #TableA as a
cross apply #TableB as b
) as M
)
select --All rows from TableB not always included
TableAID,
TableBID
from cte
where RN in (
select
top 1
iCTE.RN
from cte as iCTE
group by iCTE.RN
having count(distinct iCTE.TableBID) = (
select count(1) from #TableB
)
)
order by TableAID;
with cte as (
select
row_number() over (partition by TableAID order by newid()) as RN,
TableAID,
TableBID
from (
select
a.ID as TableAID,
b.ID as TableBID
from #TableA as a
cross apply #TableB as b
) as M
)
insert #Q
select
RN,
TableAID,
TableBID
from cte;
select * from #Q;
select --All rows from both TableA and TableB included
TableAID,
TableBID
from #Q
where RN in (
select
top 1
iQ.RN
from #Q as iQ
group by iQ.RN
having count(distinct iQ.TableBID) = (
select count(1) from #TableB
)
)
order by TableAID;
See if this gives you what you're looking for...
DECLARE
#CountA INT = (SELECT COUNT(*) FROM #TableA ta),
#CountB INT = (SELECT COUNT(*) FROM #TableB tb),
#MinCount INT;
SELECT #MinCount = CASE WHEN #CountA < #CountB THEN #CountA ELSE #CountB END;
WITH
cte_A1 AS (
SELECT
*,
rn = ROW_NUMBER() OVER (ORDER BY NEWID())
FROM
#TableA ta
),
cte_B1 AS (
SELECT
*,
rn = ROW_NUMBER() OVER (ORDER BY NEWID())
FROM
#TableB tb
),
cte_A2 AS (
SELECT
a1.ID,
rn = CASE WHEN a1.rn > #MinCount THEN a1.rn - #MinCount ELSE a1.rn end
FROM
cte_A1 a1
),
cte_B2 AS (
SELECT
b1.ID,
rn = CASE WHEN b1.rn > #MinCount THEN b1.rn - #MinCount ELSE b1.rn end
FROM
cte_B1 b1
)
SELECT
A = a.ID,
B = b.ID
FROM
cte_A2 a
JOIN cte_B2 b
ON a.rn = b.rn;
I am trying to select the top 25 parent records and join to it the first two child records ordered by date. The parent record can have 0 to n children.
The end result would be something like:
P1, C1, C2
P2, C1, C2
...
P25, C1, C2
I have found an example using max date, but I am having trouble getting a specific row number
select top 25 *
from parentTable p
left join childTable c
on p.Key = c.Key
and c.dateColumn = (
select Max(c.dateColumn)
from c
where p.Key = c.Key
)
You should be able to get what you are looking for using ROW_NUMBER():
WITH c AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY [key] ORDER BY [date] asc) as rn
,[key]
,[date]
from child
)
SELECT top 25 p.[key], c1.[key], c1.[date], c2.[key], c2.[date]
FROM parent p
LEFT JOIN c c1
ON p.[key] = c1.[key]
AND c1.rn = 1
LEFT JOIN c c2
ON p.[key] = c2.[key]
AND c2.rn = 2
Check SQLFiddle for test data/results
You can use CTE.
;with cte as (
select top (25) *
from parentTable
)
select *
from cte p
left join childTable c
on p.[Key] = c.[Key];
First solution (because data set seems to be small) one solution is to use OUTER APPLY:
select top (25) ... columns ...
from parentTable p
outer apply (
select top (2) ... columns ...
from childTable c
where p.[Key] = c.[Key]
order by c.dateColumn desc -- asc ?
) a
-- Most of the times, when top filter is used order by clause should be also used
order by p.dateColumn desc -- asc ?
-- order by p.idColumn desc -- asc ?
Second solution (could be less eficient):
select top (25) ... columns ...
from parentTable p
left join (
select top (2) ... columns ..., ROW_NUMBER() over(partition by c.[Key] order by c.dateColumn desc) as rn -- asc ?
from childTable c
) a on p.[Key] = a.[Key] and a.rn < 3
-- Most of the times, when top filter is used order by clause should be also used
order by p.dateColumn desc -- asc ?
-- order by p.idColumn desc -- asc ?
Note: at least for the first solution one of following indices could help from the point of view of performance:
create index ix_name on dbo.childTable ([Key], [dateColumn])
--or
create index ix_name on dbo.childTable ([Key], [dateColumn])
include (... columns from select top(2) clause ...)
My table has these columns
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[PersonID] [bigint] NOT NULL,
[B] [bit] NULL
Given a list of PersonID values, I would like to update the table, setting the value of B for the most recent entry for each PersonID to 1.
The below script only updates a single record, but it seems something resembling it may work?
UPDATE table
SET B = 1
WHERE PersonID in (<idlist>)
AND ID IN (Select MAX(ID) FROM table WHERE PersonID in <idlist>)
If am not wrong this is what you are trying to achieve
;with cte as
(
select row_number()over(partition by PersonID order by ID desc) as rn,*
from yourtable
Where PersonID in (<idlist>)
)
update cte set B=1
where Rn=1
Or use correlated sub-query
UPDATE A
SET A.B = 1
from table A
WHERE PersonID in (<idlist>)
AND ID = (Select Top 1 ID FROM table B WHERE A.PersonID = B.PersonID
order by id desc)
IN (Select MAX(ID) FROM ...) limits to a unique MAX(ID).
Group by PersonID in order to get the MAX(ID) for each Person.
UPDATE table
SET B = 1
WHERE
ID IN (SELECT MAX(ID)
FROM table
WHERE PersonID in (<idlist>)
GROUP BY PersonID)
Your initial script is very close. Just use correlated subquery:
UPDATE t
SET B = 1
FROM #tab t
WHERE t.PersonID in (<idlist>)
AND ID IN (SELECT MAX(ID) FROM #tab t2 WHERE t.PersonID = t2.PersonID);
LiveDemo
Assuming that id is unique, your subquery just needs a correlation clause:
UPDATE table t
SET B = 1
WHERE t.PersonID in (<idlist>) AND
ID IN (Select MAX(ID) FROM table t2 WHERE t2.PersonID = t.PersonId);
Alternatively, you could use group by, but that would probably be more expensive:
UPDATE table t
SET B = 1
WHERE t.PersonID in (<idlist>) AND
ID IN (Select MAX(ID)
FROM table t2
WHERE t2.PersonID in (<idlist>)
GROUP BY t2.PersonId
);
The use of row_number() with an updatable CTE is another reasonable approach.
I need to create a SQL-Query that uses a recursive CTE to fetch records from TableA. (Tree-Structure). I pass him the "leaf" and want to know the way back to the root.
This works with a #SOME_ID Variable
;WITH cte_recursive AS
(
SELECT ID, SUB_ID FROM tableA
WHERE SUB_ID = #SOME_ID
UNION ALL
SELECT parent.ID, parent.SUB_ID
FROM tableA parent
INNER JOIN cte_recursive child ON child.ID = parent.SUB_ID
)
What I need to acchieve now is, that I take every record from TableB
and use tableB.SOME_ID for the CTE Expression and create an insert into TableC foreach record the CTE generates plus some fields from TableB
(cte_recursive.CHILD_ID, tableB.SomeValue, tableB.SomeOtherValue)
So my question here is, how do I pass the tableB.SOME_ID to the cte expression ?
So in TableA I got something like this:
ID, SUB_ID
1 , 2
2 , 3
2 , 4
2 , 5
5 , 6
7 , 8
8 , 9
If I pass him SUB_ID = 5, the CTE returns me the Records #1, #2, #3, #4, #5
as SUB_ID = 5 is a Child of a Child of a child... of ID = 1
You can create table valued function
create function ftBranchOf
(
#SOME_ID int -- actual type of #SOME_ID
)
returns table as return
(
WITH cte_recursive AS
(
SELECT ID, SUB_ID FROM tableA
WHERE SUB_ID = #SOME_ID
UNION ALL
SELECT parent.ID, parent.SUB_ID
FROM tableA parent
INNER JOIN cte_recursive child ON child.ID = parent.SUB_ID
)
select * from cte_recursive
)
And then use it in your query
insert into TableC (...)
select p.ID, b.SomeValue, b.SomeOtherValue
from TableB b
cross apply ftBranchOf(b.SOME_ID) p
I'm not sure what are you want, so just guessing
;WITH cte_tableB AS
(
SELECT * FROM tableB
)
, cte_recursive AS
(
SELECT ID, SUB_ID, SOME_ID FROM tableA
WHERE SUB_ID IN (SELECT SOME_ID FROM cte_tableB)
UNION ALL
SELECT parent.ID, parent.SUB_ID, SOME_ID
FROM tableA parent
INNER JOIN cte_recursive child ON child.ID = parent.SUB_ID
)
INSERT [YourTable] ([YourColumns...])
SELECT [YourColumns...]
FROM cte_recursive
INNER JOIN cte_tableB ON cte_recursive.SomeID = cte_tableB.SomeID
I have a table which contains hierarchy data - something like:
childID | parentID
____________________
1 | 5
5 | 9
9 | 20
2 | 4
3 | 7
7 | 8
8 | 8
20 | 20
4 | 4
8 | 8
desired output:
I've created a recursive CTE which finds me the top fatherID.
Something like:
;WITH cte AS (
SELECT a.childID
,a.parentID
,1 AS lvl
FROM [Agent_Agents] a
WHERE a.childID = 214 //<==== value to begin with !! - thats part the problem
UNION ALL
SELECT tmp.childID
,tmp.parentID
,cte.lvl+1
FROM [Agent_Agents] tmp
INNER JOIN cte ON tmp.childID = cte.parentID
WHERE cte.childID<>cte.parentID
)
SELECT *
FROM cte
WHERE lvl = (
SELECT MAX(lvl)
FROM cte
)
The problem:
I executed the CTE with explicit childID value to begin with (214) !
So it gives me the value for 214 only.
the CTE do the recursive part and find topParent for childID.
but
I want ForEach row in the Table - to execute the CTE with the childID value !
I have tried to do it with CROSS APPLY:
Something like:
select * from myTable Cross Apply (
;WITH cte AS (....)
)
but IMHO (from my testing !!) - its impossible.
The other idea of putting the recursive CTE in a UDF has a performance penalty (udf's problem as we know).
How can I create this query so that it'll actually work? ( or some near solution )?
here is what I've tried
https://data.stackexchange.com/stackoverflow/query/edit/69458
Can't you do something like this?
;WITH cte AS (....)
SELECT
*
FROM
cte
CROSS APPLY
dbo.myTable tbl ON cte.XXX = tbl.XXX
Put the CROSS APPLY after the CTE definition - into the one SQL statement that refers back to the CTE. Wouldn't that work??
OR: - flip around your logic - do a "top-down" CTE, that picks the top-level nodes first, and then iterates through the hiearchy. This way, you can easily determine the "top-level father" in the first part of the recursive CTE - something like this:
;WITH ChildParent AS
(
SELECT
ID,
ParentID = ISNULL(ParentID, -1),
SomeName,
PLevel = 1, -- defines level, 1 = TOP, 2 = immediate child nodes etc.
TopLevelFather = ID -- define "top-level" parent node
FROM dbo.[Agent_Agents]
WHERE ParentID IS NULL
UNION ALL
SELECT
a.ID,
ParentID = ISNULL(a.ParentID, -1),
a.SomeName,
PLevel = cp.PLevel + 1,
cp.TopLevelFather -- keep selecting the same value for all child nodes
FROM dbo.[Agent_Agents] a
INNER JOIN ChildParent cp ON r.ParentID = cp.ID
)
SELECT
ID,
ParentID,
SomeName,
PLevel,
TopLevelFather
FROM ChildParent
This would give you nodes something like this (based on your sample data, slightly extended):
ID ParentID SomeName PLevel TopLevelFather
20 -1 Top#20 1 20
4 -1 TOP#4 1 4
8 -1 TOP#8 1 8
7 8 ChildID = 7 2 8
3 7 ChildID = 3 3 8
2 4 ChildID = 2 2 4
9 20 ChildID = 9 2 20
5 9 ChildID = 5 3 20
1 5 ChildID = 1 4 20
Now if you select a particular child node from this CTE output, you'll always get all the infos you need - including the "level" of the child, and its top-level parent node.
Not sure I understand what you are looking for but it could be this.
;WITH c
AS (SELECT childid,
parentid,
parentid AS topParentID
FROM #myTable
WHERE childid = parentid
UNION ALL
SELECT T.childid,
T.parentid,
c.topparentid
FROM #myTable AS T
INNER JOIN c
ON T.parentid = c.childid
WHERE T.childid <> T.parentid)
SELECT childid,
topparentid
FROM c
ORDER BY childid
SE-Data
It is the same as answer by marc_s with the difference that I use your table variable and the fact that you have childID = parentID for root nodes where the answer by marc_s has parent_ID = null for root nodes. In my opinion it is better to have parent_ID = null for root nodes.
I have not yet the time to look further into your question and am not sure whether or not i've understood your problem, but couldn't you use this svf to get the top father's id?
CREATE FUNCTION [dbo].[getTopParent] (
#ChildID INT
)
RETURNS int
AS
BEGIN
DECLARE #result int;
DECLARE #ParentID int;
SET #ParentID=(
SELECT ParentID FROM ChildParent
WHERE ChildID = #ChildID
)
IF(#ParentID IS NULL)
SET #result = #ChildID
ELSE
SET #result = [dbo].[getTopParent](#ParentID)
RETURN #result
END
Then you should be able to find each top parent in this way:
SELECT ChildID
, [dbo].[getTopParent](ChildID) AS TopParentID
FROM ChildParent
select distinct
a.ChildID,a.ParentID,
--isnull(nullif(c.parentID,b.parentID),a.parentID) as toppa,
B.parentID
--,c.parentID
,isnull(nullif(d.parentID,a.parentID),c.parentID) as toppa1,a.name
from myTable a
inner join myTable c
on a.parentID=c.parentID
inner join myTable b
on b.childID=a.parentID
inner join myTable d
on d.childID=b.parentID
I have using the without CTE expression and then using joins to get the step to step parent for child and then more important Common table expressions were introduced in SQL Server 2005 not in server 2000 so using joins to get values this is basic way for to get parentid for a child value
select dbo.[fn_getIMCatPath](8)
select Cat_id,Cat_name,dbo.[fn_getIMCatPath](cat_id) from im_category_master
Create FUNCTION [dbo].[fn_getIMCatPath] (#ID INT)
returns NVARCHAR(1000)
AS
BEGIN
DECLARE #Return NVARCHAR(1000),
#parentID INT,
#iCount INT
SET #iCount = 0
SELECT #Return = Cat_name,
#parentID = parent_id
FROM im_category_master
WHERE [cat_id] = #ID
WHILE #parentID IS NOT NULL
BEGIN
SELECT #Return = cat_name + '>' + #Return,
#parentID = parent_id
FROM im_category_master
WHERE [cat_id] = #parentID
SET #iCount = #iCount + 1
IF #parentID = -1
BEGIN
SET #parentID = NULL
END
IF #iCount > 10
BEGIN
SET #parentID = NULL
SET #Return = ''
END
END
RETURN #Return
END
Consider this sample data and respective SQL to access child records along with their top parent.
Sample DATA
SQL code:
;WITH c AS (
SELECT Id, Name, ParentId as CategoryId,
Id as MainCategoryId, Name AS MainCategory
FROM pmsItemCategory
WHERE ParentId is null
UNION ALL
SELECT T.Id, T.Name, T.ParentId, MainCategoryId, MainCategory
FROM pmsItemCategory AS T
INNER JOIN c ON T.ParentId = c.Id
WHERE T.ParentId is not null
)
SELECT Id, Name, CategoryId, MainCategoryId, MainCategory
FROM c
order by Id
select distinct
a.ChildID,a.ParentID,
--isnull(nullif(c.parentID,b.parentID),a.parentID) as toppa,
B.parentID
--,c.parentID
,isnull(nullif(d.parentID,a.parentID),c.parentID) as toppa1,a.name
from myTable a
inner join myTable c
on a.parentID=c.parentID
inner join myTable b
on b.childID=a.parentID
inner join myTable d
on d.childID=b.parentID
With cte as
(
Select ChileId,Name,ParentId from tblHerarchy
where ParentId is null
union ALL
Select h.ChileId,h.Name,h.ParentId from cte
inner join tblHerarchy h on h.ParentId=cte.ChileId
)
Select * from cte
With cteherarchy as
(
Select ChileId,Name,ParentId from tblHerarchy
where ParentId is null
union ALL
Select h.ChileId,h.Name,h.ParentId from cte
inner join tblHerarchy h on h.ParentId=cte.ChileId
)
Select * from cteherarchy