Joining two Hierarchical queries to form larger Hierarchy - sql

I have researched this and know I'm not the first to ask but I can't seem to get my head around it. I have created a simple example that I think will help me crack it if someone can provide the missing link!
I have a table of areas that contains continents and countries in a hierarchy.
I also have a table of places that contains cities and landmarks in a hierarchy. This table contains an area id column to join to the areas table.
create table areas
(
id NUMBER not null,
name VARCHAR2(200) not null,
parent_id NUMBER
);
-- Top Level
Insert into areas (id, name)
Values (1, 'Europe');
Insert into areas (id, name)
Values (2, 'Americas');
Insert into areas (id, name)
Values (3, 'Asia ex Japan');
Insert into areas (id, name)
Values (4, 'Japan');
-- Jurisdictions
Insert into areas (id, name, parent_id)
Values (5, 'UK', 1);
Insert into areas (id, name, parent_id)
Values (7, 'France', 1);
Insert into areas (id, name, parent_id)
Values (6, 'Germany', 1);
Insert into areas (id, name, parent_id)
Values (8, 'Italy', 1);
Insert into areas (id, name, parent_id)
Values (9, 'US', 2);
Insert into areas (id, name, parent_id)
Values (10, 'Australia', 3);
Insert into areas (id, name, parent_id)
Values (11, 'New Zealand', 3);
create table places
(
id NUMBER not null,
name VARCHAR2(200) not null,
area_id NUMBER,
parent_id NUMBER
);
Insert into places (id, name, area_id, parent_id)
Values (1, 'London', 5, NULL);
Insert into places (id, name, area_id, parent_id)
Values (2, 'Bath', 5, NULL);
Insert into places (id, name, area_id, parent_id)
Values (3, 'Liverpool', 5, NULL);
Insert into places (id, name, area_id, parent_id)
Values (4, 'Paris', 7, NULL);
Insert into places (id, name, area_id, parent_id)
Values (5, 'New York', 9, NULL);
Insert into places (id, name, area_id, parent_id)
Values (6, 'Chicago', 9, NULL);
Insert into places (id, name, area_id, parent_id)
Values (7, 'Kings Cross', 5, 1);
Insert into places (id, name, area_id, parent_id)
Values (8, 'Tower of London', 5, 1);
I can query these tables independently like this:
SELECT a.*, level FROM areas a
start with parent_id is null
connect by prior id = parent_id
SELECT p.*, level FROM places p
start with parent_id is null
connect by prior id = parent_id
Is someone able to show me the last step to join these into one query with four levels? I've been working with Oracle for years but somehow this never came up!
If there was no connect by prior in the places table, just a list of cities with an area id, would this be easier?
Thank you

Is it what you need?
with src as (
select 'A' type, a.id, a.name, a.parent_id, null area_id from areas a
union all
select 'P', -p.id id, p.name, -p.parent_id parent_id, area_id from places p)
select
src.*, level
from
src
start with
type = 'A' and parent_id is null
connect by
parent_id = prior id or
parent_id is null and area_id = prior id

Related

How do I retrieve releated models from subquery

I have a table with models. Model can be parent to another model (1 to 1)
type Model struct {
ID uuid.UUID`
Name string
ParentModel Model
CaseID uuid.UUID
ProjectID uuid.UUID
ChildModels []Model
}
What I am trying to achive is:
select all models with the same caseID and select all models with caseId from parantModel and childrenModels.
Is it possible with subquery?
I have tried to select with preload (parent/children) with orm, but do not really get where to go from there
Sample data:
CREATE TABLE public.model (
id int4 NOT NULL,
caption varchar NULL,
parent_id int4 NULL,
CONSTRAINT model_pk PRIMARY KEY (id)
);
INSERT INTO model (id, caption, parent_id) VALUES(1, 'Model1', NULL);
INSERT INTO model (id, caption, parent_id) VALUES(2, 'Model2', NULL);
INSERT INTO model (id, caption, parent_id) VALUES(3, 'Model3', NULL);
INSERT INTO model (id, caption, parent_id) VALUES(4, 'Model4', NULL);
INSERT INTO model (id, caption, parent_id) VALUES(5, 'Model1_1', 1);
INSERT INTO model (id, caption, parent_id) VALUES(6, 'Model1_2', 1);
INSERT INTO model (id, caption, parent_id) VALUES(7, 'Model1_3', 1);
INSERT INTO model (id, caption, parent_id) VALUES(8, 'Model1_2_1', 6);
INSERT INTO model (id, caption, parent_id) VALUES(9, 'Model1_2_2', 6);
INSERT INTO model (id, caption, parent_id) VALUES(10, 'Model4_1', 4);
Recursive Query example:
with recursive tbl(id, caption, parent_id) as (
select * from model
where id = 1
union all
select model.* from model
inner join tbl on tbl.id = model.parent_id
)
select * from tbl
Result:
id caption parent_id
1 Model1 [NULL]
5 Model1_1 1
6 Model1_2 1
7 Model1_3 1
8 Model1_2_1 6
9 Model1_2_2 6

Find value contained in the HierarchyId at any level

I need to find a particular value contained in the SQL Server HierarchyId column. The value can occur at any level. Here is a sample code to illustrate the issue:
CREATE TABLE mytable
(
Id INT NOT NULL PRIMARY KEY,
TeamName VARCHAR(20) NOT NULL,
MyHierarchyId HIERARCHYID NOT NULL
);
INSERT INTO mytable(Id, TeamName, MyHierarchyId)
VALUES (1, 'Corporate','/1/');
INSERT INTO mytable(Id, TeamName, MyHierarchyId)
VALUES (2, 'Group A','/1/2/');
INSERT INTO mytable(Id, TeamName, MyHierarchyId)
VALUES (3, 'Team X','/1/2/3/');
INSERT INTO mytable(Id, TeamName, MyHierarchyId)
VALUES (4, 'Group B','/1/4/');
INSERT INTO mytable(Id, TeamName, MyHierarchyId)
VALUES (5, 'Team Y','/1/4/5/');
INSERT INTO mytable(Id, TeamName, MyHierarchyId)
VALUES (6, 'Team Z','/1/4/6/');
Now I would like to find all the records, which are associated with the Id = 4. This means records 4, 5 and 6. I could use a brute force methods like this:
SELECT [M].[Id],
[M].[TeamName],
[M].[MyHierarchyId],
[M].[MyHierarchyId].ToString() AS Lineage
FROM [dbo].[mytable] AS [M]
WHERE [M].[MyHierarchyId].ToString() LIKE '%4%'
But I suspect this will be very inefficient. Once again, the problem is that the level of the node I am searching for is not known in advance.
Thank you for any recommendations.
You can use IsDescendantOf()
Select *
from mytable
Where MyHierarchyID.IsDescendantOf( (select MyHierarchyID from mytable where id=4) ) = 1
Results
Id TeamName MyHierarchyId
4 Group B 0x5C20
5 Team Y 0x5C3180
6 Team Z 0x5C3280

PL/SQL update all records except with max value

Please help with SQL query. I've got a table:
CREATE TABLE PCDEVUSER.tabletest
(
id INT PRIMARY KEY NOT NULL,
name VARCHAR2(64),
pattern INT DEFAULT 1 NOT NULL,
tempval INT
);
Let's pretend it was filled with values:
INSERT INTO TABLETEST (ID, NAME, PATTERN, TEMPVAL) VALUES (1, 'A', 1, 10);
INSERT INTO TABLETEST (ID, NAME, PATTERN, TEMPVAL) VALUES (2, 'A', 1, 20);
INSERT INTO TABLETEST (ID, NAME, PATTERN, TEMPVAL) VALUES (3, 'A', 2, 10);
INSERT INTO TABLETEST (ID, NAME, PATTERN, TEMPVAL) VALUES (5, 'A', 2, 20);
INSERT INTO TABLETEST (ID, NAME, PATTERN, TEMPVAL) VALUES (4, 'A', 2, 30);
And I need to update all records (grouped by pattern) with NO MAX value TEMPVALUE. So as result I have to update records with Ids (1, 3, 5). Records with IDs (2, 4) has max values in there PATTERN group.
HELP PLZ
This select statement will help you get the IDs you need :
SELECT
*
FROM
(SELECT
id
,name
,pattern
,tempval
,MAX(tempval) OVER (PARTITION BY pattern) max_tempval
FROM
tabletest
)
WHERE 1=1
AND tempval != max_tempval
;
You should be able to build an update statement around that easily enough
Something like this:
update tabletest t
set ????
where t.tempval < (select max(tempval) from tabletest tt where tt.pattern = t.pattern);
It is unclear what values you want to set. The ???? is for the code that sets the values.

Insert same data multiple times

I have an insert statement similar to this:
insert into table (id, name, descr) values (4, 'asdf', 'this is not a word');
I need to insert this same statement with multiple ids. Right now I have:
insert into table (id, name, descr) values (4, 'asdf', 'this is not a word');
insert into table (id, name, descr) values (6, 'asdf', 'this is not a word');
insert into table (id, name, descr) values (7, 'asdf', 'this is not a word');
insert into table (id, name, descr) values (9, 'asdf', 'this is not a word');
Am I just going to have to run this, or is there a more condensed version?
Use a select . . . insert:
insert into table(id, name, descr)
select i.id, 'asdf', 'this is not a word'
from (select 4 as id from dual union all
select 6 from dual union all
select 7 from dual union all
select 9 from dual
) i;
You can use the INSERT ALL statement
INSERT ALL
INTO table (id, name, descr) VALUES (4, 'asdf', 'this is not a word')
INTO table (id, name, descr) VALUES (6, 'asdf', 'this is not a word')
INTO table (id, name, descr) VALUES (7, 'asdf', 'this is not a word')
INTO table (id, name, descr) VALUES (9, 'asdf', 'this is not a word')
SELECT * FROM dual;
INSERT INTO [TableName] (id, name, descr) VALUES
(4, 'asdf', 'this is not a word'),
(6, 'asdf', 'this is not a word'),
(7, 'asdf', 'this is not a word'),
(9, 'asdf', 'this is not a word')
For the sake of argument, one could create a more permanent solution if that ID is also the primary_key by creating a sequence, adding a BEFORE INSERT trigger to the table to increment the ID using the sequence automatically, then loop, inserting however many rows you want and let the ID increment itself:
-- Create the table
CREATE TABLE SEQ_TEST
(
ST_ID NUMBER,
ST_NAME VARCHAR2(50 BYTE),
ST_DESC CHAR(100 BYTE)
);
-- Create the sequence
CREATE SEQUENCE SEQ_TEST_SEQ
START WITH 1
MAXVALUE 9999999999999999999999999999
MINVALUE 0
NOCYCLE
NOCACHE
ORDER;
-- Create the before insert trigger
CREATE OR REPLACE TRIGGER SEQ_TEST_BI
BEFORE INSERT
ON SEQ_TEST
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
BEGIN
if :old.ST_ID is null then
:new.ST_ID := SEQ_TEST_SEQ.nextval;
end if;
END SEQ_TEST_BI;
-- insert 25 rows using an anonymous block. Note the ID is NULL
-- which causes the trigger to increment ID
-- based on the sequence.
begin
for i in 1..25
loop
-- NOTE - Technically you could omit the 'ST_ID' and NULL and it would
-- still work, but I prefer to keep them here to show this action
-- of inserting NULL is intentional and show that all columns are
-- accounted for in the insert.
insert into SEQ_TEST (ST_ID, ST_NAME, ST_DESC) values (NULL, 'asdf', 'this is not a word');
end loop;
end;
commit;
-- Prove it.
select * from seq_test;

Better way SQL insert query

Actually, I don't know what is different the following query?
Which one is better(performance, etc...)? Btw, I use SQL Server.
Query 1 :
INSERT INTO PERSON (ID, NAME, ADDRESS) VALUES('001', 'Smit', 'London');
INSERT INTO PERSON (ID, NAME, ADDRESS) VALUES('002', 'Jhon', 'London');
Query 2 : I never saw before
INSERT INTO PERSON (ID, NAME, ADDRESS)
SELECT '001', 'Smit', 'London' UNION ALL
SELECT '002', 'Jhon', 'London'
How about the multi-row syntax with table value constructors:
INSERT INTO PERSON (ID, NAME, ADDRESS)
VALUES ('001', 'Smit', 'London'), ('002', 'Jhon', 'London');