Counting a root's children in SQL - sql

Let's say I have a table with 3 columns:
item ID 'ID'
parent ID 'ParentID'
item name 'Title'
Now, how should I count how many children a Root has?

SELECT COUNT(*)
FROM T
WHERE ParentID = #ParentID
If you want descendants not just immediate children you would need a recursive CTE.
;WITH R AS
(
SELECT ID
FROM T
WHERE ParentID = #RootID
UNION ALL
SELECT T.ID
FROM T
JOIN R ON R.ID = T.ParentID
)
SELECT COUNT(*)
FROM R

Related

sql - How to return NULL row from left table for each parent even when child records exist

I have 2 tables with a parent-child relation. I'd like to return a row with NULL child values for parents that have children, in addition to the child records.
For example, for tables parent and child:
SELECT parent.parentid, childid
FROM parent
LEFT JOIN child
ON parent.parentid=child.parentid
For a parent parentA with children childA1, childA2, I'd like a result:
parentA, NULL
parentA, childA1
parentA, childA2
A normal LEFT JOIN will not return the first row. A UNION query works, but I'm wondering if there is a simpler/better query, using standard SQL for Oracle and SQL Server. My UNION query is:
SELECT parent.parentid, childid
FROM parent
INNER JOIN child ON parent.parentid=child.parentid
UNION
SELECT parent.parentid, NULL
FROM parent
I think your query is better written as:
SELECT p.parentid, childid
FROM parent p INNER JOIN
child c
ON p.parentid = c.parentid
UNION ALL
SELECT p.parentid, NULL
FROM parent p;
That is UNION ALL is better than UNION, because UNION has additional overhead for removing duplicate values. If you can have duplicates in parent, just use SELECT DISTINCT in the second query.
Also, you might want to add an ORDER BY to get results in order the question seems to specify:
ORDER BY parentid, (CASE WHEN child is NULL then 0 else 1 end), chile
An alternative way of doing it would be
SELECT parent.parentid,
childid
FROM parent
LEFT JOIN child
ON parent.parentid = child.parentid
GROUP BY GROUPING SETS( ( parent.parentid ), ( parent.parentid, childid ) )
HAVING (childid IS NOT NULL) OR GROUPING(childid) =1
ORDER BY parentid ASC,
GROUPING(childid) DESC
If you only want the row for parents with children you should use an inner join
SQL Fiddle - Oracle
SQL Fiddle - SQL Server

How to do conditional update on columns using CTE?

I have a table CUST with following layout. There are no constraints. I do see that one ChildID has more than one ParentID associated with it. (Please see the records for ChildID = 115)
Here is what I need -
Wherever one child has more than 1 parent, I want to update those ParentID and ParentName with the ParentID and ParentName which has max match_per. So in the below image, I want ParentID 1111 and ParentName LEE YOUNG WOOK to update all records where ChildId = 115 (since the match_per 0.96 is maximum within the given set). In case there are two parents with equal max match_per, then I want to pick any 1 one of them.
I know it is possible using CTE but I don't know how to update CTE. Can anybody help?
One way of doing it
WITH CTE1 AS
(
SELECT *,
CASE WHEN match_per =
MAX(match_per) OVER (PARTITION BY ChildId)
THEN CAST(ParentId AS CHAR(10)) + ParentName
END AS parentDetailsForMax
FROM CUST
), CTE2 AS
(
SELECT *,
MAX(parentDetailsForMax) OVER (PARTITION BY ChildId) AS maxParentDetailsForMax
FROM CTE1
)
UPDATE CTE2
SET ParentId = CAST(LEFT(maxParentDetailsForMax,10) AS int),
ParentName = SUBSTRING(maxParentDetailsForMax,10,8000)
Getting both the parent id and parent name is a bit tricky. I think the logic is easiest using cross apply:
with toupdate as (
select t.*, p.parentId as new_parentId, p.parentName as new_parentName
max(match_per) over (partition by childid) as max_match_per,
count(*) over (partition by childid) as numparents
from table t cross apply
(select top 1 p.*
from table p
where p.childid = t.childid
order by match_per desc
) p
)
update toupdate
set parentId = new_ParentId,
parentName = new_ParentName
where numparents > 1;
As a note: the fact that parent id and parent name are both stored in the table, potentially multiple times seems like a problem. I would expect to look up the name, given the id, to reduce data redundancy.
Try something like this?? The first CTE will get Max(match_per) for each ChildID. Then, the second will use the new MaxMatchPer to find what its corresponding ParentID should be.
; WITH CTE AS (
SELECT ChildID,MAX(match_per) AS MaxMatchPer
FROM tbl
GROUP BY ChildID
), CTE1 AS (
SELECT t.ParentID, c.ChildID
FROM tbl t
JOIN CTE c
ON c.ChildID = t.ChildID
AND c.MaxMatchPer = t.match_per
)
UPDATE t
SET ParentID = c.ParentID
FROM tbl t
LEFT JOIN CTE1 c
ON c.ChildID = t.ChildID
Also, this is poor normalization. You should not have ParentName nor ChildName in this table.

(How) Can I place a with sql query in a table value function?

I would like to put the following with query as a return of a table value function.
;with org as (
select ID
from ORG_UNIT
where id = #ID_ORG_UNIT
union all
select PARENT_ID
from ORG_UNIT c
join org p on p.ID = c.id
)
select * from org
I already tried to create such a function however I'm now resorting to stored procedure in despair...
Is it possible to create a function to return this result?
create function test_func(#ID_ORG_UNIT int)
returns table
as
return (
with org as (
select ID
from ORG_UNIT
where id = #ID_ORG_UNIT
union all
select PARENT_ID
from ORG_UNIT c
join org p on p.ID = c.id
)
select * from org
);
SQLFiddle

How to get all child of a given id in SQL Server query

I have two tables in SQL Server database:
category(
itemid,
parentid
)
ArticleAssignedCategories(
categid,
artid
)
categid is a foreign key of itemid
I want to get count of artids and child of that for given itemid (child means categories with parentid of given itemid.)
For example; If given itemid = 1 and in table category have (3,1),(4,1)(5,3)
All of 3, 4, 5 are child of 1
Can anyone help me to write a good query?
Recursive queries can be done using CTE
with CTE(itemid, parentid)
as (
-- start with some category
select itemid, parentid
from category where itemid = <some_itemid>
union all
-- recursively add children
select c.itemid, c.parentid
from category c
join CTE on c.parentid = CTE.itemid
)
select count(*)
from ArticleAssignedCategories a
join CTE on CTE.itemid = a.categid
Here is the query. I hope this may help you
select b.artid,count(b.artid) from category a
inner join ArticleAssignedCategories b on a.itemid = b.artid
group by b.artid

Recursive query in SQL Server

I have a table with following structure
Table name: matches
That basically stores which product is matching which product. I need to process this table
And store in a groups table like below.
Table Name: groups
group_ID stores the MIN Product_ID of the Product_IDS that form a group. To give an example let's say
If A is matching B and B is Matching C then three rows should go to group table in format (A, A), (A, B), (A, C)
I have tried looking into co-related subqueries and CTE, but not getting this to implement.
I need to do this all in SQL.
Thanks for the help .
Try this:
;WITH CTE
AS
(
SELECT DISTINCT
M1.Product_ID Group_ID,
M1.Product_ID
FROM matches M1
LEFT JOIN matches M2
ON M1.Product_Id = M2.matching_Product_Id
WHERE M2.matching_Product_Id IS NULL
UNION ALL
SELECT
C.Group_ID,
M.matching_Product_Id
FROM CTE C
JOIN matches M
ON C.Product_ID = M.Product_ID
)
SELECT * FROM CTE ORDER BY Group_ID
You can use OPTION(MAXRECURSION n) to control recursion depth.
SQL FIDDLE DEMO
Something like this (not tested)
with match_groups as (
select product_id,
matching_product_id,
product_id as group_id
from matches
where product_id not in (select matching_product_id from matches)
union all
select m.product_id, m.matching_product_id, p.group_id
from matches m
join match_groups p on m.product_id = p.matching_product_id
)
select group_id, product_id
from match_groups
order by group_id;
Sample of the Recursive Level:
DECLARE #VALUE_CODE AS VARCHAR(5);
--SET #VALUE_CODE = 'A' -- Specify a level
WITH ViewValue AS
(
SELECT ValueCode
, ValueDesc
, PrecedingValueCode
FROM ValuesTable
WHERE PrecedingValueCode IS NULL
UNION ALL
SELECT A.ValueCode
, A.ValueDesc
, A.PrecedingValueCode
FROM ValuesTable A
INNER JOIN ViewValue V ON
V.ValueCode = A.PrecedingValueCode
)
SELECT ValueCode, ValueDesc, PrecedingValueCode
FROM ViewValue
--WHERE PrecedingValueCode = #VALUE_CODE -- Specific level
--WHERE PrecedingValueCode IS NULL -- Root