Confusing use of cte ,inner join and union all - sql

I am confused about the use of inner join on the CTE here. what is in a as appears in the inner join at the end and what is in cte1 c?
WITH cte1 AS
(SELECT id,geographyname,
OriginalGoals,
ParentGeographyname,
0 AS HierarchyLevel,
paradigm
FROM businessobject_RefinementMaster
WHERE Id = #Geo
UNION ALL
SELECT a.id,
a.geographyname,
a.OriginalGoals,
a.ParentGeographyName,
HierarchyLevel-1 AS HierarchyLevel,
a.paradigm
FROM businessobject_RefinementMaster a
INNER JOIN cte1 c ON c.ParentGeographyname = a.geographyname
AND c.paradigm=a.paradigm )
what will be the result of this query?

This is a recursive CTE (hidden-RBAR). I'll try to comment it in a way, that you can understand, what is going on:
WITH cte1 AS
(
/*
This is called "anchor" and reads the "head" lines of a hierarchy
*/
SELECT id,
geographyname,
OriginalGoals,
ParentGeographyname,
0 AS HierarchyLevel, --obviously this starts with a "0"
paradigm
FROM businessobject_RefinementMaster --The source-table
WHERE Id = #Geo --You read elements with Id=#Geo. This is - probably - one single element
--The next SELECT will be "added" to the result-set
UNION ALL
/*
The column-list must be absolutely the same (count and type) of the anchor
*/
SELECT a.id,
a.geographyname,
a.OriginalGoals,
a.ParentGeographyName,
HierarchyLevel-1 AS HierarchyLevel, --this is simple counting. Started with 0 this will lead to -1, -2, -3...
a.paradigm
FROM businessobject_RefinementMaster a --same source-table as above
INNER JOIN cte1 c ON c.ParentGeographyname = a.geographyname --Find rows where the name of the element is the parent-name of the former element
AND c.paradigm=a.paradigm
)
/*
Return the result-set
*/
SELECT * FROM cte1
The result should be a full recursive list of parents to a given element.

Related

Use Exists with a Column of Query Result?

I have 2 tables.
One is bom_master:
CHILD
PARENT
1-111
66-6666
2-222
77-7777
2-222
88-8888
3-333
99-9999
Another one is library:
FileName
Location
66-6666_A.step
S:\ABC
77-7777_C~K1.step
S:\DEF
And I want to find out if the child's parents have related files in the library.
Expected Result:
CHILD
PARENT
FileName
1-111
66-6666
66-6666_A.step
2-222
77-7777
77-7777_C~K1.step
Tried below lines but return no results. Any comments? Thank you.
WITH temp_parent_PN(parentPN)
AS
(
SELECT
[PARENT]
FROM [bom_master]
where [bom_master].[CHILD] in ('1-111','2-222')
)
SELECT s.[filename]
FROM [library] s
WHERE EXISTS
(
SELECT
*
FROM temp_parent_PN b
where s.[filename] LIKE '%'+b.[parentPN]+'%'
)
If you have just one level of dependencies use the join solution proposed by dimis164.
If you have deeper levels you could use recursive queries allowed by WITH clause (
ref. WITH common_table_expression (Transact-SQL)).
This is a sample with one more level of relation in bom_master (you could then join the result of the recursive query with library as you need).
DECLARE #bom_master TABLE (Child NVARCHAR(MAX), Parent NVARCHAR(MAX));
INSERT INTO #bom_master VALUES
('1-111', '66-666'),
('2-222', '77-777'),
('3-333', '88-888'),
('4-444', '99-999'),
('A-AAA', '1-111');
WITH
leaf AS ( -- Get the leaf elements (elements without a child)
SELECT Child FROM #bom_master b1
WHERE NOT EXISTS (SELECT * FROM #bom_master b2 WHERE b2.Parent = b1.Child) ),
rec(Child, Parent, Level) AS (
SELECT b.Child, b.Parent, Level = 1
FROM #bom_master b
JOIN leaf l ON l.Child = b.Child
UNION ALL
SELECT rec.Child, b.Parent, Level = rec.Level + 1
FROM rec
JOIN #bom_master b
ON b.Child = rec.Parent )
SELECT * FROM rec
I think you don't have to use exists. The problem is that you need to substring to match the join.
Have a look at this:
SELECT b.CHILD, b.PARENT, l.[FileName]
FROM [bom_master] b
INNER JOIN [library] l ON b.PARENT = SUBSTRING(l.FileName,1,7)

Using SQL recursive query as AND statement

I have the following flowchart. Hopefully, it's self-explanatory
On top of the hierarchy there's a request that is a basic parent of all the request below it. Requests below have the 'id', 'parent_id', 'state' fields
My final goal is to find all parents ids that satisfy all AND statements including the last one (hierarchical query). However, I don't know how to use it as an AND statement.
The hierarchical query looks like this:
with cte
as (select id, state
from tbl_request as rH
WHERE id = /* each id from the very first select */
UNION ALL
select rH.id, rH.state
from tbl_request as rH
join cte
on rH.parent_id = cte.id
and (cte.state is null or cte.state NOT IN('not-legit'))
)
select case when exists(select 1 from cte where cte.state IN('not-legit'))
then 1 else 0 end
Expectantly, it does what it's supposed to
The solution was suggested in the question
Return true/false in recursive SQL query based on condition
For your convenience, here's a SQL Fiddle
I think I've worked out what you want.
You need to recurse through all the nodes and their children, returning its state and its ultimate root parent_id.
Then aggregate by that ID and exclude any group that contains a row with state = 'not-legit'. In other words, flip the logic to a double negative.
WITH cte AS (
SELECT rH.id, rH.state, rH.id AS top_parent
FROM tbl_request as rH
WHERE (rH.state is null or rH.state <> 'not-legit')
AND rH.parent_id IS NULL
UNION ALL
SELECT rH.id, rH.state, cte.top_parent
FROM tbl_request as rH
JOIN cte
ON rH.parent_id = cte.id
)
SELECT top_parent
FROM cte
GROUP BY
cte.top_parent
HAVING COUNT(CASE WHEN cte.state = 'not-legit' THEN 1 END) = 0;
You could also change the logic back to a positive, but it would need to look like this:
HAVING COUNT(CASE WHEN cte.state is null or cte.state <> 'not-legit' THEN 1 END) = COUNT(*)
In other words, there are the same number of these filtered rows as there are all rows.
This feels more complex than what I have put above.
SQL Fiddle
Replace your
WHERE id = /* each id from the very first select */
by
WHERE id in (
SELECT r.id FROM tbl_request AS r
/* there's also an INNER JOIN (hopefully, it won't be an obstacle) */
WHERE r.parent_id is null
/* a lot of AND statements */
)
Also, you should use UNION instead of UNION ALL, since there is no point using duplicated tuples (id and state) in this case.
To summarize, your query should look like this one
with cte
as (select id, state
from tbl_request as rH
WHERE id in (
SELECT r.id
FROM tbl_request AS r
/* there's also an INNER JOIN (hopefully, it won't be an obstacle) */
WHERE r.parent_id is null
/* a lot of AND statements */
) UNION
select rH.id, rH.state
from tbl_request as rH
join cte
on rH.parent_id = cte.id
and (cte.state is null or cte.state NOT IN('not-legit'))
)
Your subquery can contain any inner joins or any number of AND operators you need, as long as it returns one column (select r.id) it will work fine.

How to write this recursive CTE for SQL Server?

For simplicity my schema:
Folders table (FolderId, ParentFolderId)
DeviceFolderProperties table (FolderId, LogDataRetentionDaysEvent)
Not every folder has a retention day. However this is an inherited value. How can you write something in SQL to return every folder and its retention day and if that is null its inherited value.
There are multiple levels to inheritance, so it will need to walk the tree.
This is what I have tried:
;
WITH [cte]
AS
(
SELECT f.FolderId, f.ParentFolderId, dfp.LogDataRetentionDaysEvent
FROM [Folders] f
LEFT JOIN DeviceFolderProperties dfp
ON f.FolderId = dfp.FolderId
),
[cte_collapse]
AS --recurse where r days is null
(
SELECT c.FolderId, c.ParentFolderId, c.LogDataRetentionDaysEvent
FROM [cte] c
WHERE c.LogDataRetentionDaysEvent IS NULL
UNION ALL
SELECT c.FolderId, c.ParentFolderId, ISNULL(c.LogDataRetentionDaysEvent, cc.LogDataRetentionDaysEvent)
FROM [cte] c
JOIN [cte_collapse] cc ON cc.FolderId = c.ParentFolderId
)
SELECT
*
FROM
[cte_collapse]
You could write this as:
with
data as (
select f.FolderID, f.ParentFolderId, dfp.LogDataRetentionDaysEvent
from Folders f
left join DeviceFolderProperties dfp on dfp.FolderID = f.FolderID
),
cte as (
select d.*, FolderID OriginalFolderId
from data d
union all
select d.*, c.OriginalFolderId
from cte c
inner join data d on d.FolderID = c.ParentFolderId
where c.LogDataRetentionDaysEvent is null
)
select OriginalFolderId, max(LogDataRetentionDaysEvent) LogDataRetentionDaysEvent
from cte
group by OriginalFolderId
We start by generating a derived table that contains information from both tables. Then, for each record, the recursive query climbs up the hierarchy, searching for a non-null the retention at each level. The trick is to stop as soon as a match is met.

Replace no result

I have a query like this:
SELECT TV.Descrizione as TipoVers,
sum(ImportoVersamento) as ImpTot,
count(*) as N,
month(DataAllibramento) as Mese
FROM PROC_Versamento V
left outer join dbo.PROC_TipoVersamento TV
on V.IDTipoVersamento = TV.IDTipoVersamento
inner join dbo.PROC_PraticaRiscossione PR
on V.IDPraticaRiscossioneAssociata = PR.IDPratica
inner join dbo.DA_Avviso A
on PR.IDDatiAvviso = A.IDAvviso
where DataAllibramento between '2012-09-08' and '2012-09-17' and A.IDFornitura = 4
group by V.IDTipoVersamento,month(DataAllibramento),TV.Descrizione
order by V.IDTipoVersamento,month(DataAllibramento)
This query must always return something. If no result is produced a
0 0 0 0
row must be returned. How can I do this. Use a isnull for every selected field isn't usefull.
Use a derived table with one row and do a outer apply to your other table / query.
Here is a sample with a table variable #T in place of your real table.
declare #T table
(
ID int,
Grp int
)
select isnull(Q.MaxID, 0) as MaxID,
isnull(Q.C, 0) as C
from (select 1) as T(X)
outer apply (
-- Your query goes here
select max(ID) as MaxID,
count(*) as C
from #T
group by Grp
) as Q
order by Q.C -- order by goes to the outer query
That will make sure you have always at least one row in the output.
Something like this using your query.
select isnull(Q.TipoVers, '0') as TipoVers,
isnull(Q.ImpTot, 0) as ImpTot,
isnull(Q.N, 0) as N,
isnull(Q.Mese, 0) as Mese
from (select 1) as T(X)
outer apply (
SELECT TV.Descrizione as TipoVers,
sum(ImportoVersamento) as ImpTot,
count(*) as N,
month(DataAllibramento) as Mese,
V.IDTipoVersamento
FROM PROC_Versamento V
left outer join dbo.PROC_TipoVersamento TV
on V.IDTipoVersamento = TV.IDTipoVersamento
inner join dbo.PROC_PraticaRiscossione PR
on V.IDPraticaRiscossioneAssociata = PR.IDPratica
inner join dbo.DA_Avviso A
on PR.IDDatiAvviso = A.IDAvviso
where DataAllibramento between '2012-09-08' and '2012-09-17' and A.IDFornitura = 4
group by V.IDTipoVersamento,month(DataAllibramento),TV.Descrizione
) as Q
order by Q.IDTipoVersamento, Q.Mese
Use COALESCE. It returns the first non-null value. E.g.
SELECT COALESCE(TV.Desc, 0)...
Will return 0 if TV.DESC is NULL.
You can try:
with dat as (select TV.[Desc] as TipyDesc, sum(Import) as ToImp, count(*) as N, month(Date) as Mounth
from /*DATA SOURCE HERE*/ as TV
group by [Desc], month(Date))
select [TipyDesc], ToImp, N, Mounth from dat
union all
select '0', 0, 0, 0 where (select count (*) from dat)=0
That should do what you want...
If it's ok to include the "0 0 0 0" row in a result set that has data, you can use a union:
SELECT TV.Desc as TipyDesc,
sum(Import) as TotImp,
count(*) as N,
month(Date) as Mounth
...
UNION
SELECT
0,0,0,0
Depending on the database, you may need a FROM for the second SELECT. In Oracle, this would be "FROM DUAL". For MySQL, no FROM is necessary

Find leaf nodes in hierarchical tree

I have a table in my database which stores a tree structure. Here are the relevant fields:
mytree (id, parentid, otherfields...)
I want to find all the leaf nodes (that is, any record whose id is not another record's parentid)
I've tried this:
SELECT * FROM mytree WHERE `id` NOT IN (SELECT DISTINCT `parentid` FROM `mytree`)
But that returned an empty set. Strangely, removing the "NOT" returns the set of all the non-leaf nodes.
Can anyone see where I'm going wrong?
Update: Thanks for the answers folks, they all have been correct and worked for me. I've accepted Daniel's since it also explains why my query didn't work (the NULL thing).
Your query didn't work because the sub-query includes NULL. The following slight modification works for me:
SELECT * FROM `mytree` WHERE `id` NOT IN (
SELECT DISTINCT `parentid` FROM `mytree` WHERE `parentid` IS NOT NULL)
No clue why your query didn't work. Here's the identical thing in left outer join syntax - try it this way?
select a.*
from mytree a left outer join
mytree b on a.id = b.parentid
where b.parentid is null
SELECT * FROM mytree AS t1
LEFT JOIN mytree AS t2 ON t1.id=t2.parentid
WHERE t2.parentid IS NULL
Select * from mytree where id not in (Select distinct parentid from mytree where parentid is not null)
http://archives.postgresql.org/pgsql-sql/2005-10/msg00228.php
select *
from `mytree `
where not exists (select *
from `mytree ` as `nodes`
where `categories`.`id` = `nodes`.`parent`)
my table structure is
memberid MemberID joiningposition packagetype
RPM00000 NULL Root free
RPM71572 RPM00000 Left Royal
RPM323768 RPM00000 Right Royal
RPM715790 RPM71572 Left free
RPM323769 RPM71572 Right free
RPM715987 RPM323768 Left free
RPM323985 RPM323768 Right free
RPM733333 RPM323985 Right free
RPM324444 RPM715987 *emphasized text*Right Royal
--
ALTER procedure [dbo].[sunnypro]
as
DECLARE #pId varchar(40) = 'RPM00000';
Declare #Id int
set #Id=(select id from registration where childid=#pId)
begin
-- Recursive CTE
WITH R AS
(
SELECT
BU.DateofJoing,
BU.childid,
BU.joiningposition,
BU.packagetype
FROM registration AS BU
WHERE
BU.MemberID = #pId and
BU.joiningposition IN ('Left', 'Right')
or BU.packagetype in('Royal','Platinum','Majestic')
and BU.Id>#id
UNION All
-- Recursive part
SELECT
BU.DateofJoing,
BU.childid,
R.joiningposition,
BU.packagetype
FROM R
JOIN registration AS BU
ON BU.MemberID = R.childid
WHERE
BU.joiningposition IN ('Left', 'Right') and
BU.packagetype in('Royal','Platinum','Majestic')
and BU.Id>#id
)
INSERT INTO Wallatpayout
(childid
,packagetype
,joiningposition
,DateofJoing
,Total)
-- Final groups of nodes found
SELECT top 3
R.childid,
R.packagetype,
R.joiningposition,
R.DateofJoing,
Total = COUNT_BIG(*)
FROM R where R.packagetype in('Royal','Platinum','Majestic')
GROUP BY R.childid,
R.joiningposition,
R.DateofJoing,
R.packagetype
OPTION (MAXRECURSION 0);
end