I have a table with applications and table with messages. Applications has hierarchical structure, e.g each application has a parent. And I have a table with messages. Each message has a key and an application id. I want to be able to select message by it's key. If it's found for current application then return it, if no then try to find it with parent id.
Apps table:
id | name | parentId
--------------------
1 |parent| NULL
2 |child | 1
Msg table:
key | text | app
-------------------------------------------------
overriden.system.msg | some text | 1
parent.msg | parent txt | 1
overriden.system.msg | overriden text | 2
So if I'm in the child app(2) on key overriden.system.msg I want to get overriden text. On key parent.msg I want to get parent txt. I know that it must be done with cte, but I have very little expirience with sql and cte is mind-blowing for me right now. Could you please provide working query for this situation? Or maybe you have a better vision how to achieve such functionality without recursion?
The above should work fine:
with app_tree (id, app, lvl) as
( select id , parentID , 0
from Apps
where id = 2
union all
select t.app, a.parentID, t.lvl + 1 from
Apps a
join app_tree t
on t.app = a.id ),
all_msg as (
select key, text,
row_number() over ( partition by key order by lvl) overrideLevel
from app_tree a
join msg m
on m.app = a.id )
select * from all_msg where
overrideLevel =1
It returns:
KEY TEXT
-------------------------------------
overriden.system.msg overriden text
parent.msg parent txt
First it gets the list of all applications for specified id using recursive query with parentid. After that it gets a list of all functions for all applications and generates increasing numbers for the same keys basing on the level. At the end it just takes the first possible level and ignore all parent's levels for the same key.
Mb it will help you:
with app (id, name, parent_id) as
(select 1, 'parent', null from dual union all
select 2, 'child', 1 from dual)
,msg (key, text, app) as
(select 'overriden.system.msg', 'some text', 1 from dual union all
select 'parent.msg', 'parent txt', 1 from dual union all
select 'overriden.system.msg', 'overriden text', 2 from dual)
select key
,max(text) keep (dense_rank last order by nvl2(text,level,0)) msg
from
(select *
from app a
join msg m on (a.id = m.app)) v
start with v.parent_id is null
connect by prior v.id = v.parent_id and prior v.key = v.key
group by key
Related
Working on a query right now where we want to understand which business is referring the most downstream orders for us. I've put together a very basic table for demonstration purposes here with 4 businesses listed. Bar and Donut were both ultimately referred by Foo and I want to be able to show Foo as a business has generated X number of orders. Obviously getting the the single referral for Foo (from Bar) and Bar (from Donut) are simple joins. But how do you go from Bar to get back to Foo?
I'll add that I've done some more googling this AM and found a few very similar questions about the top level parent and most of the responses suggest recursive CTE. It's been awhile since I've dug deep into SQL stuff, but 8 years ago I know these were not overly popular. Is there another way around this? Perhaps better to just store that parent ID on the order table at the time of order?
+----+--------+--------------------+
| Id | Name | ReferralBusinessId |
+----+--------+--------------------+
| 1 | Foo | |
| 2 | Bar | 1 |
| 3 | Donut | 2 |
| 4 | Coffee | |
+----+--------+--------------------+
WITH RECURSIVE entity_hierarchy AS (
SELECT id, name, parent FROM entities WHERE name = 'Donut'
UNION
SELECT e.id, e.name, e.parent FROM entities e INNER JOIN entity_hierarchy eh on e.id = eh.parent
)
SELECT id, name, parent FROM entity_hierarchy;
SQL Fiddle Example
Assuming you're using SQL Server, you could use a query like the one below to generate a hierarchical Id path for a particular business.
declare #tbl as table (Id int, Name varchar(30), ReferralBusinessId int)
insert into #tbl (id, Name, ReferralBusinessId) values
(1, 'Foo', null),
(2, 'Bar', 1),
(3, 'Donut', 2),
(4, 'Coffee', null);
;WITH business AS (
SELECT Id, Name, ReferralBusinessId
, 0 AS Level
, CAST(Id AS VARCHAR(255)) AS Path
FROM #tbl
UNION ALL
SELECT R.Id, R.Name, R.ReferralBusinessId
, Level + 1
, CAST(Path + '.' + CAST(R.Id AS VARCHAR(255)) AS VARCHAR(255))
FROM #tbl R
INNER JOIN business b ON b.Id = R.ReferralBusinessId
)
SELECT * FROM business ORDER BY Path
In the following example
id parent_id
A A
B A
C B
select id, parent_id
from table
start with id = 'A'
connect by nocycle parent_id = prior id
I get
A A
B A
C B
In my database I have millions of rows in the table and deep and wide hierarchies and I'm not interested in all children. I can derive the children I'm interested in. So I want to turn the query on its head and supply START WITH with the children ids. I then want to output the parent recursively until I get to the top. In my case the top is where the id and parent_id are equal. This is what I'm trying, but I can't get it to show the top level parent.
select id, parent_id
from table
START WITH id = 'C'
CONNECT BY nocycle id = PRIOR parent_id
This gives me
C B
B A
It's not outputting the A A. Is it possible to do this? What I'm hoping to do is not show the parent_id as a separate column in the output, but just show the name relating to the id. The hierarchy is then implied by the order.
I got that result by using WITH clause.
WITH REC_TABLE ( ID, PARENT_ID)
AS
(
--Start WITH
SELECT ID, PARENT_ID
FROM table
WHERE ID='C'
UNION ALL
--Recursive Block
SELECT T.ID, T.PARENT_ID
FROM table T
JOIN REC_TABLE R
ON R.PARENT_ID=T.ID
AND R.PARENT_ID!=R.ID --NoCycle rule
)
SELECT *
FROM REC_TABLE;
And it seems to work that way too.
select id, parent_id
from T
START WITH id = 'C'
CONNECT BY id = PRIOR parent_id and parent_id!= prior id;
-- ^^^^^^^^^^^^^^^^^^^^
-- break cycles
Hope it helps.
I'm not very experienced with advance SQL queries, I'm familiar with basic statements and basic joins, currently trying to figure out how to write a query that seems to be out of my depth and I haven't been able to find a solution from google so far and I'm hoping somebody might be able to point me in the right direction.
The table I'm working with has an ID column, and a 'parent id' column.
I'm looking for all descendants of ID '1' - rows with a parent ID of '1', rows with a parent ID equal to any row's ID with a parent ID of '1' etc. Currently I've been doing this manually but there are hundreds of descendants so far and I feel like there's a way to put this into one query.
Any help would be appreciated, if this is unclear I can also try to clarify.
EDIT - I got it working with the following query:
with cteMappings as (
select map_id, parent_map_id, map_name
from admin_map
where map_id = '1'
union all
select a.map_id, a.parent_map_id, a.map_name
from admin_map a
inner join cteMappings m
on a.parent_map_id = m.map_id
)
select map_id, parent_map_id, map_name
from cteMappings
Sounds like it can be achieved by Common Table Expression:
DECLARE #temp TABLE (id INT IDENTITY(1, 1), parent_id INT);
INSERT #temp
SELECT NULL
UNION ALL
SELECT 1
UNION ALL
SELECT 2
UNION ALL
SELECT NULL
SELECT * FROM #temp
; WITH HierarchyTemp (id, parent_id, depth) AS (
SELECT id, parent_id, 0
FROM #temp
WHERE id = 1
UNION ALL
SELECT t.id, t.parent_id, ht.depth + 1
FROM #temp t
INNER JOIN HierarchyTemp ht ON ht.id = t.parent_id
)
SELECT *
FROM HierarchyTemp
So the above example is creating a table variable with 4 rows:
id parent_id
1 NULL
2 1
3 2
4 NULL
Result of descendent of id '1' (also including itself, but can be excluded with an additional WHERE clause):
id parent_id depth
1 NULL 0
2 1 1
3 2 2
I have table with such structure.
ElementId | ParentId
-------------------
1 | NULL
2 | 1
3 | 2
4 | 3
Let say current element has Id 4. I want to select all parent ids.
Result should be: 3, 2, 1
How I can do it? DB is MSSQL
You can use recursive queries for this: http://msdn.microsoft.com/en-us/library/aa175801(SQL.80).aspx
You can use it like this:
with Hierachy(ElementID, ParentID, Level) as (
select ElementID, ParentID, 0 as Level
from table t
where t.ElementID = X -- insert parameter here
union all
select t.ElementID, t.ParentID, th.Level + 1
from table t
inner join Hierachy th
on t.ParentId = th.ElementID
)
select ElementID, ParentID
from Hierachy
where Level > 0
I think it might be easiest to do the following:
while parent != NULL
get parent of current element
I can't think of any way of doing this in plain SQL that wouldn't cause issues on larger databases.
if you want pure sql try:
select ParentId from myTable Desc
that would work in mysql... you might need to modify the Desc (sort in descending order) part
I have a tree in my database that is stored using parent id links.
A sample of what I have for data in the table is:
id | name | parent id
---+-------------+-----------
0 | root | NULL
1 | Node 1 | 0
2 | Node 2 | 0
3 | Node 1.1 | 1
4 | Node 1.1.1 | 3
5 | Node 1.1.2 | 3
Now I would like to get a list of all the direct descendants of a given node but if none exist I would like to have it just return the node itself.
I want the return for the query for children of id = 3 to be:
children
--------
4
5
Then the query for the children of id = 4 to be:
children
--------
4
I can change the way I am storing the tree to a nested set but I don't see how that would make the query I want possible.
In new PostgreSQL 8.4 you can do it with a CTE:
WITH RECURSIVE q AS
(
SELECT h, 1 AS level, ARRAY[id] AS breadcrumb
FROM t_hierarchy h
WHERE parent = 0
UNION ALL
SELECT hi, q.level + 1 AS level, breadcrumb || id
FROM q
JOIN t_hierarchy hi
ON hi.parent = (q.h).id
)
SELECT REPEAT(' ', level) || (q.h).id,
(q.h).parent,
(q.h).value,
level,
breadcrumb::VARCHAR AS path
FROM q
ORDER BY
breadcrumb
See this article in my blog for details:
PostgreSQL 8.4: preserving order for hierarchical query
In 8.3 or earlier, you'll have to write a function:
CREATE TYPE tp_hierarchy AS (node t_hierarchy, level INT);
CREATE OR REPLACE FUNCTION fn_hierarchy_connect_by(INT, INT)
RETURNS SETOF tp_hierarchy
AS
$$
SELECT CASE
WHEN node = 1 THEN
(t_hierarchy, $2)::tp_hierarchy
ELSE
fn_hierarchy_connect_by((q.t_hierarchy).id, $2 + 1)
END
FROM (
SELECT t_hierarchy, node
FROM (
SELECT 1 AS node
UNION ALL
SELECT 2
) nodes,
t_hierarchy
WHERE parent = $1
ORDER BY
id, node
) q;
$$
LANGUAGE 'sql';
and select from this function:
SELECT *
FROM fn_hierarchy_connect_by(4, 1)
The first parameter is the root id, the second should be 1.
See this article in my blog for more detail:
Hierarchical queries in PostgreSQL
Update:
To show only the first level children, or the node itself if the children do not exist, issue this query:
SELECT *
FROM t_hierarchy
WHERE parent = #start
UNION ALL
SELECT *
FROM t_hierarchy
WHERE id = #start
AND NOT EXISTS
(
SELECT NULL
FROM t_hierarchy
WHERE parent = #start
)
This is more efficient than a JOIN, since the second query will take but two index scans at most: the first one to make sure to find out if a child exists, the second one to select the parent row if no children exist.
Found a query that works the way I wanted.
SELECT * FROM
( SELECT id FROM t_tree WHERE name = '' ) AS i,
t_tree g
WHERE
( ( i.id = g.id ) AND
NOT EXISTS ( SELECT * FROM t_tree WHERE parentid = i.id ) ) OR
( ( i.id = g.parentid ) AND
EXISTS ( SELECT * FROM t_tree WHERE parentid = i.id ) )