Merge is not returning right data - oracle

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.

Related

Hierarchical query find all parents with some filters

I've a structure like:
CREATE TABLE BUSINESS_UNIT (
ID NUMBER,
DESCPRIPTION VARCHAR2(100 CHAR),
TAG VARCHAR2(10 CHAR)
);
CREATE TABLE USERS (
ID NUMBER,
LOGIN VARCHAR2(10 CHAR)
);
CREATE TABLE BU_USERS (
ID_BU NUMBER,
ID_USER NUMBER
);
CREATE TABLE BU_TREE (
ID_BU NUMBER,
ID_PARENT NUMBER
);
INSERT INTO BUSINESS_UNIT VALUES(1, 'ONE', 'WHITE');
INSERT INTO BUSINESS_UNIT VALUES(2, 'TWO', 'RED');
INSERT INTO BUSINESS_UNIT VALUES(3, 'THREE', 'YELLOW');
INSERT INTO BUSINESS_UNIT VALUES(4, 'FOUR', 'GREEN');
INSERT INTO BUSINESS_UNIT VALUES(5, 'FIVE', 'GREEN');
INSERT INTO BUSINESS_UNIT VALUES(6, 'SIX', 'RED');
INSERT INTO BUSINESS_UNIT VALUES(7, 'SEVEN', 'GREEN');
INSERT INTO USERS VALUES(1, 'USER1');
INSERT INTO BU_USERS VALUES(5, 1);
/*
___1w___
___2r___ 3y___
__4g 7g 6r
5g
*/
INSERT INTO BU_TREE VALUES(5, 4);
INSERT INTO BU_TREE VALUES(4, 2);
INSERT INTO BU_TREE VALUES(7, 2);
INSERT INTO BU_TREE VALUES(2, 1);
INSERT INTO BU_TREE VALUES(3, 1);
INSERT INTO BU_TREE VALUES(6, 3);
And I have to get parent record with TAG = "RED".
I tried something like:
SELECT
B.ID
FROM BUSINESS_UNIT B
INNER JOIN BU_TREE T ON (B.ID = T.ID_BU)
START WITH B.ID = (SELECT ID_BU FROM BU_USERS WHERE ID_USER = 1)
CONNECT BY PRIOR B.ID = T.ID_PARENT
But it returns only the "directly" father, that is the 5.
So, for the user one I have to get its parent red business unit: the number two.
Why my query doesn't returns all the parents?
Thanks
Why my query doesn't returns all the parents?
Well, it does return all the parents.
But there is no any parents that meet the condition CONNECT BY PRIOR B.ID = T.ID_PARENT, so the query shows only 1 record.
Please look at the below query, it shows the whole resultset of the join:
SELECT *
FROM BUSINESS_UNIT B
inner JOIN BU_TREE T ON (B.ID = T.ID_BU);
ID DESCPRIPTI TAG ID_BU ID_PARENT
---------- ---------- ---------- ---------- ----------
2 TWO RED 2 1
3 THREE YELLOW 3 1
4 FOUR GREEN 4 2
5 FIVE GREEN 5 4
6 SIX RED 6 3
7 SEVEN GREEN 7 2
The subquery (SELECT ID_BU FROM BU_USERS WHERE ID_USER = 1) gives 5, so your query is equivalent to:
SELECT *
FROM BUSINESS_UNIT B
inner JOIN BU_TREE T ON (B.ID = T.ID_BU);
START WITH B.ID = 5
CONNECT BY PRIOR B.ID = T.ID_PARENT;
WHen running this query, in the frst step Oracle evaluates START WITH B.ID = 5 conditions, and picks rows that meet this condition, that is:
ID DESCPRIPTI TAG ID_BU ID_PARENT
---------- ---------- ---------- ---------- ----------
5 FIVE GREEN 5 4
In the next step, oracle applies CONNECT BY PRIOR B.ID = T.ID_PARENT; condition to the above "prior" record (taking PRIOR ID = 5 from this record), and then search for records that meet the right side of the condition (5 = ID_PARENT) in the resultset of join.
There is no any record with parent_id = 5 in the result of join, so the final result of the connect-by query is:
ID ID_BU ID_PARENT
---------- ---------- ----------
5 5 4

Hierarchical Queries inside Join

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

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

Resources