Hierarchical Queries inside Join - oracle

I'm facing one annoying problema and I would like some help.
This is the situation.
CREATE TABLE tree_hierarchy (
id NUMBER (20)
,parent_id NUMBER (20)
);
CREATE TABLE tree_information (
id NUMBER (20)
,some_text VARCHAR(20)
,tree_id NUMBER (20)
);
INSERT INTO tree_hierarchy (id, parent_id) VALUES (2, null);
INSERT INTO tree_hierarchy (id, parent_id) VALUES (4, 2);
INSERT INTO tree_hierarchy (id, parent_id) VALUES (9, 4);
INSERT INTO tree_hierarchy (id, parent_id) VALUES (20, null);
INSERT INTO tree_hierarchy (id, parent_id) VALUES (40, 20);
INSERT INTO tree_hierarchy (id, parent_id) VALUES (90, 40);
INSERT INTO tree_information (id, some_text, tree_id) VALUES (10,'Some teste', 2);
INSERT INTO tree_information (id, some_text, tree_id) VALUES (11,'Other tree', 20);
And i would like to do something like this.
SELECT hier.*
FROM tree_information Ti
JOIN (
SELECT
id,
parent_id
FROM tree_hierarchy th
where connect_by_isleaf = 1
START WITH th.id = ti.tree_id
CONNECT BY PRIOR th.id = th.parent_id
) hier on 1=1;
but ti.tree_id is not visible inside the select.
If I change the start with condition for
START WITH th.parent_id is null
Will stay wrong.
Someone has idea how to solve this situation ?

I would appreciate if you provide expected result explicitly.
My best guess is:
SELECT hier.*, ti.*
FROM tree_information Ti
JOIN (
SELECT
id,
parent_id,
connect_by_root th.id as tree_id
FROM tree_hierarchy th
where connect_by_isleaf = 1
START WITH th.id in ( select tii.tree_id from tree_information Tii)
CONNECT BY PRIOR th.id = th.parent_id
) hier on ti.tree_id = hier.tree_id;
ID PARENT_ID TREE_ID ID SOME_TEXT TREE_ID
9 4 2 10 Some teste 2
90 40 20 11 Other tree 20

Related

post-filter the results in oracle

What would be the most performant oracle query (not a procedure) for searching the jobs which have only inactive employees for the below tables. *I cannot change DB structure.
Job table
id
name
0
j0
1
j1
2
j2
3
j3
4
j4
...
JoinTable table
id
job_id
employee_id
0
0
0
1
0
1
2
1
0
3
1
2
4
2
2
5
2
3
6
3
2
7
3
4
8
4
3
...
Employee table
id
status
0
active
1
active
2
inactive
3
inactive
4
null
...
Result should be like:
name
j2
j4
Create and inserts:
CREATE TABLE job(
id int,
name char(50)
);
CREATE TABLE jointable(
id int,
job_id int,
employee_id int
);
CREATE TABLE employee(
id int,
status char(50)
);
INSERT INTO job (id, name) VALUES (0, 'j0');
INSERT INTO job (id, name) VALUES (1, 'j1');
INSERT INTO job (id, name) VALUES (2, 'j2');
INSERT INTO job (id, name) VALUES (3, 'j3');
INSERT INTO job (id, name) VALUES (4, 'j4');
INSERT INTO employee (id, status) VALUES (0, 'active');
INSERT INTO employee (id, status) VALUES (1, 'active');
INSERT INTO employee (id, status) VALUES (2, 'inactive');
INSERT INTO employee (id, status) VALUES (3, 'inactive');
INSERT INTO employee (id, status) VALUES (4, null);
INSERT INTO jointable (id, job_id, employee_id) VALUES (0, 0, 0);
INSERT INTO jointable (id, job_id, employee_id) VALUES (1, 0, 1);
INSERT INTO jointable (id, job_id, employee_id) VALUES (2, 1, 0);
INSERT INTO jointable (id, job_id, employee_id) VALUES (3, 1, 2);
INSERT INTO jointable (id, job_id, employee_id) VALUES (4, 2, 2);
INSERT INTO jointable (id, job_id, employee_id) VALUES (5, 2, 3);
INSERT INTO jointable (id, job_id, employee_id) VALUES (6, 3, 2);
INSERT INTO jointable (id, job_id, employee_id) VALUES (7, 3, 4);
INSERT INTO jointable (id, job_id, employee_id) VALUES (8, 4, 3);
I've tried something like:
select j.name from job j
left join jointable jt on j.id = jt.job_id
left join employee e on jt.employee_id = e.id
where e.status = 'inactive'
-- where not exists(select status from employee where status <> active and status <> null)
-- where exists(select status from employee where status = 'inactive')
order by j.id desc;
I'm not sure how to incroporate in, having, count or proper where clauses to achive the reqirement.. or maybe i should use something else. Thanks in advance.
Maybe a little more performant than Ankit's query as this one fetches data from each of your tables only once.
SQL> with temp as
2 (select j.name,
3 min(nvl(e.status, '-')) min_status,
4 max(nvl(e.status, '-')) max_status
5 from job j join jointable t on t.job_id = j.id
6 join employee e on e.id = t.employee_id
7 group by j.name
8 )
9 select x.name
10 from temp x
11 where x.min_status = x.max_status
12 and x.min_status = 'inactive';
NAME
----
j2
j4
SQL>
I think you may try below query -
SELECT DISTINCT J.name
FROM Job J
JOIN JoinTable JT ON J.id = JT.job_id
JOIN Employee E ON JT.employee_id = E.id
WHERE E.status = 'inactive'
AND NOT EXISTS (SELECT NULL
FROM Employee E2
JOIN JoinTable JT2 ON JT2.employee_id = E2.id
WHERE COALESCE(E2.status, 'I') <> 'inactive'
AND J.id = JT2.job_id);
Fiddle Demo.

How to bind horizontal values of a table to a vertical values of another table in oracle database

i have 2 tables .
The columns start with attributes are change based on department. the description of attributes are here
My requirement is to get the values of each attributes with its primary key based on the department as table bellow.
Honestly i am stuck on this problem in my program. I have no permission to change the tables and there is no common unique key column.i would appreciate if anyone could provide me a suggestion.
with a as (
select a.*, row_number() over (partition by department order by attributeID) rn
from attributes a),
e as (
select employeeId, department, attribute1, 1 rn from employees union all
select employeeId, department, attribute2, 2 rn from employees union all
select employeeId, department, attribute3, 3 rn from employees
)
select e.employeeId, a.attributeid, e.department, a.attribute, a.meaning,
e.attribute1 as value
from e join a on a.department=e.department and a.rn=e.rn
order by e.employeeId, a.attributeid
Test data and output:
create table employees (employeeID number(3), name varchar2(10), department varchar2(5), age number(3), attribute1 varchar2(10), attribute2 varchar2(10), attribute3 varchar2(10));
insert into employees values (1, 'john', 'IT', 22, 'attr1val1', 'attr2val2', null);
insert into employees values (2, 'jane', 'HR', 32, 'attr1val3', 'attr2val4', 'attr3val5');
insert into employees values (3, 'joe', 'HR', 23, 'attr1val6', 'attr2val7', 'attr3val8');
insert into employees values (4, 'jack', 'IT', 45, 'attr1val9', 'attr2val10', null);
create table attributes (attributeID number(3), department varchar2(10), attribute varchar2(10), meaning varchar2(10));
insert into attributes values (1, 'IT', 'attribute1', 'laptoptype');
insert into attributes values (2, 'IT', 'attribute2', 'networkloc');
insert into attributes values (3, 'HR', 'attribute1', 'location');
insert into attributes values (4, 'HR', 'attribute2', 'position');
insert into attributes values (5, 'HR', 'attribute3', 'allocation');
EMPLOYEEID ATTRIBUTEID DEPARTMENT ATTRIBUTE MEANING VALUE
---------- ----------- ---------- ---------- ---------- ----------
1 1 IT attribute1 laptoptype attr1val1
1 2 IT attribute2 networkloc attr2val2
2 3 HR attribute1 location attr1val3
2 4 HR attribute2 position attr2val4
2 5 HR attribute3 allocation attr3val5
3 3 HR attribute1 location attr1val6
3 4 HR attribute2 position attr2val7
3 5 HR attribute3 allocation attr3val8
4 1 IT attribute1 laptoptype attr1val9
4 2 IT attribute2 networkloc attr2val10
Edit: Explanation
In answer I used with
clause just to divide solution into readable steps. You can move them into from clause of main query if it is
more comfortable for you. Anyway: subquery a reads data from table attributes and adds number for rows,
so for each department they are allways numbered from 1. I used row_number() for that. Subquery e unions (all) required attributes and numbers
them accordingly. Numbers generated in both subqueries are then used in main join: a.department=e.department and a.rn=e.rn.
Alternative 1 - if you are using Oracle 11g you could use the unpivot. See what is generated by subquery, and how it is joined with attributes table:
with e as (
select employeeId, name, department, attribute, value from employees
unpivot (value for attribute in ("ATTRIBUTE1", "ATTRIBUTE2", "ATTRIBUTE3"))
)
select e.employeeId, a.attributeid, e.department, a.attribute,
a.meaning, e.value
from e join attributes a on a.department=e.department
and lower(a.attribute)=lower(e.attribute)
order by e.employeeId, a.attributeid;
Alternative 2 - with hierarchical subquery generator (subquery r), realised by connect by which simple creates numbers from 1, 2, 3 which are next joined with employees and proper attribute
is attached as value in case clause. Rest is made in similiar way like in original answer.
with a as (
select a.*, row_number() over (partition by department order by attributeID) rn
from attributes a),
r as (select level rn from dual connect by level<=3),
e as (
select employeeId, department, rn,
case when r.rn = 1 then attribute1
when r.rn = 2 then attribute2
when r.rn = 3 then attribute3
end value
from employees cross join r
)
select e.employeeId, a.attributeid, e.department, a.attribute,
a.meaning, e.value
from e join a on a.department=e.department and a.rn=e.rn
order by e.employeeId, a.attributeid
All three versions gave me the same output. I also tested first option on similiar table with 100k rows and get output in few seconds (for 5 attributes). Please test all solutions and try to understand them. If you can use unpivot version I would prefer this.
Sorry for delayed explanation and any language mistakes.
The WITH clause was added with Oracle 9.2 and should do the trick. For the other attributes just add more sub queries where the filter is att.attribute = 'attribute2' or 'Attribute3'...
WITH e AS
(SELECT emp.employee_ID, emp.department, emp.attribute1
FROM employee emp),
a AS (SELECT att.attribute_id, att.attribute, att.meaning
FROM attribute_TYPE att
WHERE att.attribute = 'attribute1')a
SELECT e.employeeid, att.attributeid, e.department, a.attribute,
a.meaning e.attribute1
FROM e JOIN a ON e.department = a.department

Merge statement

I am looking into merge statements, but I can not figure out the syntax.
Let's say I have these tables
CREATE TABLE employee (
employee_id NUMBER(5),
first_name VARCHAR2(20),
last_name VARCHAR2(20),
dept_no NUMBER(2),
salary NUMBER(10));
INSERT INTO employee VALUES (1, 'Dan', 'Morgan', 10, 100000);
INSERT INTO employee VALUES (2, 'Helen', 'Lofstrom', 20, 100000);
INSERT INTO employee VALUES (3, 'Akiko', 'Toyota', 20, 50000);
INSERT INTO employee VALUES (4, 'Jackie', 'Stough', 20, 40000);
INSERT INTO employee VALUES (5, 'Richard', 'Foote', 20, 70000);
INSERT INTO employee VALUES (6, 'Joe', 'Johnson', 20, 30000);
INSERT INTO employee VALUES (7, 'Clark', 'Urling', 20, 90000);
CREATE TABLE bonuses (
employee_id NUMBER, bonus NUMBER DEFAULT 100);
INSERT INTO bonuses (employee_id) VALUES (1);
INSERT INTO bonuses (employee_id) VALUES (2);
INSERT INTO bonuses (employee_id) VALUES (4);
INSERT INTO bonuses (employee_id) VALUES (6);
INSERT INTO bonuses (employee_id) VALUES (7);
COMMIT;
and just for the sake of an example I want to update all entries of bonuses with 200 * employee_id. Here is my statement. What's wrong with it?
merge into bonuses b
using
(select employee_id id, 200 bonus from employees) test
on (test.id = b.employee_id)
when matched then update set
b.bonus = test.bonus * test.employee_id
Thanks!
P.S. See also my sqlfiddle here: http://sqlfiddle.com/#!4/ff425/14
merge into bonuses b
using
(select employee_id id, 200 bonus from employee) test
on (test.id = b.employee_id)
when matched then update set
b.bonus = test.bonus * test.id;
Two minor mistakes:
1) employee instead of employees
2) test.id instead of test.employee_id

PL/SQL Displaying Results

I have difficulty writing a PL/SQL Statement to display the following output.
These are my tables:
Employee (E#, FName, LName,...)
SkillPossessed (E#, SName, Level) (Employee may possessed more than 1 skills)
I need to display the output as follow:
1 Mary Loo:Sewing,Dancing,Cooking
2 Peter Pan:Painting, Singing
...
How do I do it?
Thanks
No need for PL/SQL, just use LISTAGG.
create table employee (id number, fname varchar2(20), lname varchar2(20), primary key (id));
create table skillpossessed(employee_id number, sname varchar2(20), lvl number, constraint fk_employee_id foreign key(employee_id) references employee(id));
insert into employee values (1, 'Mary', 'Loo');
insert into employee values (2, 'Peter', 'Pan');
insert into skillpossessed values (1, 'Sewing', 1);
insert into skillpossessed values (1, 'Dancing', 2);
insert into skillpossessed values (1, 'Cooking', 5);
insert into skillpossessed values (2, 'Painting', 3);
insert into skillpossessed values (2, 'Singing', 1);
select e.id, e.fname, e.lname, listagg (s.sname, ',')
within group (order by s.sname) as skills
from employee e, skillpossessed s
where e.id = s.employee_id group by e.id, e.fname, e.lname;
ID FNAME LNAME SKILLS
---------- ------ ----- ----------------------
1 Mary Loo Cooking,Dancing,Sewing
2 Peter Pan Painting,Singing

Merge is not returning right data

I have 2 tables:
CREATE TABLE employee (
employee_id NUMBER(5),
first_name VARCHAR2(20),
last_name VARCHAR2(20),
dept_no NUMBER(2),
salary NUMBER(10));
Table employee has values:
(1, 'Dan', 'Morgan', 10, 100000);
(2, 'Helen', 'Lofstrom', 20, 100000);
(3, 'Akiko', 'Toyota', 20, 50000);
(4, 'Jackie', 'Stough', 20, 40000);
(5, 'Richard', 'Foote', 20, 70000);
(6, 'Joe', 'Johnson', 20, 30000);
(7, 'Clark', 'Urling', 20, 90000);
CREATE TABLE bonuses (
employee_id NUMBER, bonus NUMBER DEFAULT 100);
Table bonuses has values:
(1,100);
(2,100);
(4,100);
(6,100);
(7,100);
And I did merge based on bonuses table:
MERGE INTO bonuses b
USING (
SELECT employee_id, salary, dept_no
FROM employee
WHERE dept_no =20) e
ON (b.employee_id = e.employee_id)
WHEN MATCHED THEN
UPDATE SET b.bonus = e.salary * 0.1
DELETE WHERE (e.salary < 40000)
WHEN NOT MATCHED THEN
INSERT (b.employee_id, b.bonus)
VALUES (e.employee_id, e.salary * 0.05)
WHERE (e.salary > 40000);
And when I
select * from bonuses;
EMPLOYEE_ID BONUS
------------------
1 100
2 10000
3 2500
4 4000
5 3500
7 9000
My question is "where is my bonuses record for employee_id #6"?
Deleted by DELETE WHERE (e.salary < 40000) statement?.
And You should probably make one of you conditions to include equality.
After all someone salary may be just 40K just like Jackie's.

Resources