Hierarchical SQL query not returning level - sql-server

I have a typical SQL Server hierarchical query:
WITH bhp AS (
SELECT name, 0 AS level
FROM dbo.BhpNode
WHERE parent_id IS NULL
UNION ALL
SELECT a.name, level + 1
FROM dbo.BhpNode a
INNER JOIN dbo.BhpNode b
ON b.bhp_node_id = a.parent_id )
SELECT * FROM bhp
This seems to match the various examples of hierarchical queries I've found around the web, but for some reason it's producting this error:
Msg 207, Level 16, State 1, Line 12
Invalid column name 'level'.
I'm sure I'm missing something obvious, but I've stared at it too long to see it. Any idea where I'm going wrong?

Your query isn't recursive - you have to select from bhp inside the second part of the recursive CTE. Try this instead:
WITH bhp AS (
SELECT *, 0 AS [level]
FROM dbo.BhpNode
WHERE parent_id IS NULL
UNION ALL
SELECT b.*, [level] + 1
FROM bhp a
INNER JOIN dbo.BhpNode b
ON a.bhp_node_id = b.parent_id)
SELECT * FROM bhp

In the recursive section of the CTE, one of the tables you reference should be the CTE itself, shouldn't it? At the moment you are just self-joining BhpNode, and it doesn't have a level column itself.

Related

Count all max number value in difference tables sql

I got an error when I tried to solve this problem. First I need to count all values of 2 tables then I need in where condition get all max values.
My code:
Select *
FROM (
select Operator.OperatoriausPavadinimas,
(
select count(*)
from Plan
where Plan.operatoriausID= Operator.operatoriausID
) as NumberOFPlans
from Operator
)a
where a.NumberOFPlans= Max(a.NumberOFPlans)
I get this error
Msg 147, Level 15, State 1, Line 19
An aggregate may not appear in the WHERE clause unless it is in a subquery contained in a HAVING clause or a select list, and the column being aggregated is an outer reference.
I don't know how to solve this.
I need get this http://prntscr.com/p700w9
Update 1
Plan table contains of http://prntscr.com/p7055l values and
Operator table contains of http://prntscr.com/p705k0 values.
Are you looking for... an aggregate query that joins both tables and returns the record that has the maximum count?
I suspect that this might phrase as follows:
SELECT TOP(1) o.OperatoriausPavadinimas, COUNT(*)
FROM Operatorius o
INNER JOIN Planas p ON p.operatoriausID = o.operatoriausID
GROUP BY o.OperatoriausPavadinimas
ORDER BY COUNT(*) DESC
If you want to allow ties, you can use TOP(1) WITH TIES.
You can use top with ties. Your query is a bit hard to follow, but I think you want:
select top (1) with ties o.OperatoriausPavadinimas, count(*)
from plan p join
operator o
on p.operatoriausID = o.operatoriausID
group by o.OperatoriausPavadinimas
order by count(*) desc;

ORDER BY with TOP 100 PERCENT clause not working properly on the outer query. Replacing UNION with UNION ALL has effect on ORDERING of results

I have the below code:
select levelname from (
SELECT top 100 percent levelname, levelnum from CTE where 2>1 order by levelnum
UNION
select distinct TOP 100 PERCENT PL.LevelNumAndName, pl.levelnum Level from Pyramid_level pl join Pyramid_unit pu on
pl.levelnum = pu.levelnum and pl.clientnum = pu.clientnum
where pl.clientnum in ('001000', '000043') and 2 = 1
order by PL.levelnum
)A
CTE is just a CTE which I am leaving out because I don't think is necessary. It has the requisite columns.
If you look at the Last snippet, you would see that the portion after UNION has the below in WHERE clause:
and 2 = 1
So obviously this should not be executing. But below is my observation:
If I remove the part after UNION, the results are coming properly sorted.
If I keep that part(which should not be executing), the results are not correctly sorted.
If I keep that part and replace UNION with UNION ALL, it seems thatORDER BY clause is effective again.
Someone please help me in understanding what's happening here!
EDIT: UNION and UNION ALL are with respect to the code I pasted, NOT inside CTE. CTE is just a plain select with the columns levelname and levelnum in it.

How does a recursive CTE eliminate duplicates?

I'm learning recursive CTEs in the AdventureWorks2012 database using SQL Server 2014 Express. I think I'm mostly getting the below example (taking from Beginning T-SQL 3rd Edition), but I don't quite understand why the recursive CTE doesn't produce duplicates.
Below is the recursive CTE that I'm trying to understand, it's a standard employee - manager hierarchy.
;with orgchart (employeeid, managerid, title, level, node) as (
--Anchor
select employeeid
, managerid
, title
, 0
, convert(varchar(30),'/') 'node'
from employee
where managerid is null
union all
--Recursive
select emp.employeeid
, emp.managerid
, emp.title
, oc.level + 1
, convert(varchar(30), oc.node + convert(varchar(30),emp.managerid) + '/')
from employee emp
inner join orgchart oc on oc.employeeid = emp.managerid
)
select employeeid
, managerid
, space(level * 3) + title 'title'
, level
, node
from orgchart
order by node;
It works fine, but the question comes when I try to understand what's going on by recreating it via temp tables. I create a series of temp tables to plug one output into the next query's input and recreate what the recursive CTE does.
--Anchor (Level 0)
select employeeid
, managerid
, title
, 0
, convert(varchar(30),'/') 'node'
into #orgchart
from employee
where managerid is null
Then I use that temp table to recreate the first level of recursion, at this point it's just the recursive CTE but with temp tables.
--Anchor + 1 level
select *
into #orgchart2
from #orgchart
union all
select emp.employeeid
, emp.managerid
, emp.title
, oc.level + 1
, convert(varchar(30), oc.node + convert(varchar(30),emp.managerid) + '/')
from employee emp
inner join #orgchart oc on oc.employeeid = emp.managerid
So far so good, the results make sense. Then I do it one more time, but here's where it starts to break down:
--Anchor + 2 levels
select *
into #orgchart3
from #orgchart2
union all
select emp.employeeid
, emp.managerid
, emp.title
, oc.level + 1
, convert(varchar(30), oc.node + convert(varchar(30),emp.managerid) + '/')
from employee emp
inner join #orgchart2 oc on oc.employeeid = emp.managerid
The output from this begins to return duplicate rows (all fields duplicate) of the level 1 employees. This makes sense - the second query after the UNION ALL will return the previous levels as well as the new level of recursion, and UNION ALL doesn't duplicate. If I do another round of recursion, the level 2 employees are also duplicated, and so on.
I understand that I can change UNION ALL to UNION in order to remove duplicates, but I'm trying to understand why the recursive CTE doesn't produce duplicates as well? It uses UNION ALL so I don't understand where the deduplication comes in. Is removal of duplicates an intrinsic part of a recursive CTE?
I'm trying to post all the result sets, but if they're needed to understand the problem then let me know and I will post them. Thanks in advance.
The difference is that when you populate your #orgchart2, you are including all the rows from #orgchart. So now when you create #orgchart3 (which represents a 3rd level of recursion), you are joining on the rows from #orgchart as well as #orgchart2.
So when you create the third level in #orgchart3, it is related to rows in both #orgchart and #orgchart2, when it should only be related to #orgchart2. Instead your third level includes rows that are one level beyond the 2nd level, but also one level beyond the anchor level, so you are duplicating rows, since you already have rows in the second level that are one level beyond the anchor level.
The optimizer knows not to do that with recursive CTEs. Each level of recursion only looks at the previous one and ignores all the ones that came before it. So no duplicates are created.
You would simulate what the optimizer does if you left out the top half of the UNION ALL when you populated #orgchart2 and #orgchart3, and then finally produced a single UNION ALL of all three temp tables.

SQL Server 2012 CTE Find Root or Top Parent of Hierarchical Data

I'm having an issue trying to recursively walk a hierarchy to find the top node of all descendent nodes in an organizational structure that may have multiple top-level nodes. I'm trying to use a SQL Server 2012 CTE to do so, but it won't recurse to reach the very top node of each branch. I've tried writing my query EXACTLY as shown in other posts relating to this, but still no dice. (At least I think I am.) I'm hoping someone can tell me what I'm doing wrong here? This post most closely relates to what I'm trying to do and I've followed the accepted answers, but I'm still just not "getting it" : Finding a Top Level Parent in SQL
As shown above, I have OrgGroups that reference direct parent groups, unless it's a top level and then it's NULL. For instance, (4) Finance (top-level) -> (5) HR -> (11) Benefits
I want to create a database view that lists each OrgGroup along with the ID of their TOP-MOST ancestor. (not their direct parent)
So, for example, the DB View would have a record for the (11) Benefits OrgGroup and a corresponding column value for it's top-most parentgroupId of (4) Finance.
;WITH OrgStructureIndex AS
(
SELECT O.OrgGroupId, O.Name, O.OrgStructureId, O.ParentGroupId, 1 AS Lvl
FROM OrgGroups O
UNION ALL
SELECT OG.OrgGroupId, OG.Name, OG.OrgStructureId, OG.ParentGroupId, Lvl+1 AS Lvl
FROM OrgGroups OG INNER JOIN OrgStructureIndex OI
ON OI.OrgGroupId = OG.ParentGroupId
)
SELECT * FROM OrgStructureIndex
This results in the Benefits org group having a top-most ParentGroupId of (5) HR. Desired results would be (4) Finance. It also results in duplicate records.
To get rid of the duplicates at least, I've changed my SQL to:
;WITH OrgStructureIndex AS
(
SELECT O.OrgGroupId, O.Name, O.OrgStructureId, O.ParentGroupId, 1 AS Lvl
FROM OrgGroups O
UNION ALL
SELECT OG.OrgGroupId, OG.Name, OG.OrgStructureId, OG.ParentGroupId, Lvl+1 AS Lvl
FROM OrgGroups OG INNER JOIN OrgStructureIndex OI
ON OI.OrgGroupId = OG.ParentGroupId
)
,CTE_RN AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY oi.OrgGroupId ORDER BY oi.Lvl DESC) RN
FROM OrgStructureIndex oi
)
SELECT * FROM CTE_RN
WHERE RN = 1
Where am I falling short here?? TIA
Two shortcomings:
First, for some reason you decided to select all nodes in the anchor part of the CTE, not just the root ones. That's why you have a lot of duplicates.
Second, you don't pass along the only field you actually need - the Id of the actual root.
Here is how you can fix them:
;WITH OrgStructureIndex AS
(
SELECT O.OrgGroupId, O.Name, O.OrgStructureId, O.ParentGroupId, 1 AS Lvl,
-- #2
o.OrgGroupId as [RootGroupId]
FROM OrgGroups O
-- #1
where o.ParentGroupId is null
UNION ALL
SELECT OG.OrgGroupId, OG.Name, OG.OrgStructureId, OG.ParentGroupId, Lvl+1 AS Lvl,
-- #2
oi.RootGroupId
FROM OrgGroups OG INNER JOIN OrgStructureIndex OI
ON OI.OrgGroupId = OG.ParentGroupId
)
SELECT * FROM OrgStructureIndex;
You can indeed walk up from the leaf node--as you are doing--to find the root of each original row. The thing your missing is tracking the starting leaf as you recurse up. Stripped down example:
fiddle
CREATE TABLE OrgGroup (OrgGroupId INT, Name VARCHAR(10), ParentGroupId INT)
GO
INSERT INTO OrgGroup VALUES
(1,'Main', NULL),
(2,'IT',1),
(3,'DotCom',2),
(4,'Finance', NULL),
(5,'HR',4),
(6,'Accounting',4)
GO
;WITH cte AS
(
SELECT 1 AS Lvl
,OrgGroupId LeafId
,OrgGroupId
,ParentGroupId
,Name
,Name LeafName
FROM OrgGroup
UNION ALL
SELECT Lvl+1 AS Lvl
,OI.LeafId
,OG.OrgGroupId
,OG.ParentGroupId
,OG.Name
,OI.LeafName
FROM OrgGroup OG
INNER JOIN
cte OI ON OI.ParentGroupId = OG.OrgGroupId
)
,cte_rn AS (
SELECT *
,ROW_NUMBER() OVER (PARTITION BY LeafID ORDER BY Lvl DESC) rn
FROM cte
)
SELECT * FROM cte_rn WHERE rn = 1*

Common Table Expression basic example

I have a recursive tree database table
DataItem
Id (uniqueidentifier)
Parent_Id? (uniqueidentifier)
PositionInParent (int)
I've read some articles about Common Table Expressions, which allows me to recursively read the tree structure directly from SQL database, but all of them are very complicated and i cannot make them work.
I am trying to read recursively all the DataItems, starting from the root ones (which has no parent), and adding the children items (ordered by PositionInParent)
Please help me create this simple example, and from there i will add more logic if necessary.
;WITH HierarchyCTE (ParentId, Id, Level)
AS
(
SELECT e.ParentId, e.Id, 0 AS Level
FROM Employees AS e
WHERE ParentId IS NULL
UNION ALL
SELECT e.ParentId, e.Id, Level + 1
FROM Employees AS e
INNER JOIN HierarchyCTE AS h
ON e.ParentId = h.Id
)
SELECT ParentId, Id, Level AS PositionInParent
FROM HierarchyCTE
You can use condition WHERE ParentId = 0if ParentId of super parent is 0

Resources