Update with 2 joins - oracle

I'm trying to update data in 2 distinct tables in the same query with the following code:
UPDATE (SELECT s.passNumb, a.hour, a.minute, p.number, s.situation
FROM passwords s
JOIN atend a ON s.passNumb = a.passNumb
JOIN points p ON a.number = p.number
WHERE s.passNumb = 1 AND
p.number = 1)
SET a.hour = TO_CHAR(SYSDATE, 'HH24'),
a.minute = TO_CHAR(SYSDATE, 'MI'),
s.situation = 'F';
But I'm getting this error: Cannot modify a column which maps to a non key-preserved table. What am I doing wrong?

A view with a join (or an inline view containing a join in your case) must meet the following conditions to be updatable:
https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_8004.htm
If you want a join view to be updatable, then all of the following
conditions must be true:
The DML statement must affect only one table underlying the join.
For an INSERT statement, the view must not be created WITH CHECK
OPTION, and all columns into which values are inserted must come from
a key-preserved table. A key-preserved table is one for which every
primary key or unique key value in the base table is also unique in
the join view.
For an UPDATE statement, the view must not be created WITH CHECK
OPTION, and all columns updated must be extracted from a key-preserved
table.
The first condition is rather obvious: The DML statement must affect only one table underlying the join.
But what does it mean: "key preserved table" ?
A key-preserved table is one for which every primary key or unique
key value in the base table is also unique in the join view.
The key preserved table means that each row from this table will appear at most once in the result of a view.
Consider a simple example:
CREATE TABLE users(
user_id int primary key,
user_name varchar(100),
age int
);
insert into users values(1,'Tom', 22);
CREATE TABLE emails(
user_id int,
email varchar(100)
);
Insert into emails values( 1, 'tom#somedomain.com' );
Insert into emails values( 1, 'tom#www.example.org' );
commit;
And a join:
SELECT *
FROM users u
JOIN emails e ON u.user_id = e.user_id;
USER_ID USER_NAME AGE USER_ID EMAIL
---------- --------------- ---------- ---------- --------------------
1 Tom 22 1 tom#somedomain.com
1 Tom 22 1 tom#www.example.org
If you look at a result of this join, it is apparent that:
user_id, user_name and age come from non-key preserved table
email comes from key-preserved table
And now: this update is acceptable, because it updates a key preserved column (table) in this join:
UPDATE (
SELECT * FROM users u
JOIN emails e ON u.user_id = e.user_id
)
SET email = email || '.it' ;
USER_ID USER_NAME AGE USER_ID EMAIL
---------- --------------- ---------- ---------- -------------------------
1 Tom 22 1 tom#somedomain.com.it
1 Tom 22 1 tom#www.example.org.it
But this update cannot be done, since it touches a column from non-key preserved table:
UPDATE (
SELECT * FROM users u
JOIN emails e ON u.user_id = e.user_id
)
SET age = age + 2;
SQL Error: ORA-01779: cannot modify a column which maps to a non key-preserved table
01779. 00000 - "cannot modify a column which maps to a non key-preserved table"
*Cause: An attempt was made to insert or update columns of a join view which
map to a non-key-preserved table.
*Action: Modify the underlying base tables directly.
If you think a while .... Tom appears 2 times in the result of the join (but there is only one Tom in the users table).
When we are trying to update age = age + 2 in this join, then what should be a result of this update ?
Should Tom be updated only once ?
Should Tom be 22+2 = 24 years old after this update ?
Or maybe Tom should be updated twice (since it appears twice in the result of the join) so it should be 22 + 2 + 2 = 26 years old.
Another example - please tell me what should be an outcome of this update?:
UPDATE ( ....our join ... ) SET age = length( email );
There are very difficult questions :)
And because of this Oracle prevents from updating non-key preserved tables.
The error message gives this hint:
*Action: Modify the underlying base tables directly.
This means that we must update this table directly, using a separate UPDATE command:
UPDATE users SET age = age + 2

Related

to update a column details into base table 2 from column of staging table against the datails of column from base table 1

There's total of three tables involved. one header base table, one material
base table, one staging table.
I have created the staging table with 4 columns, the values will be
updated from csv uploaded, column 1 is batch_no, column 2 is for
attribute.
>header base table(h) has batch_no and batch_id
>material base table(m) has batch_id, attr_m (empty, to be updated)
>staging table(s) has batch_no and attr_s
create table he (BATCH_ID number, BATCH_NO varchar2(30));
create table me (a6 varchar2(30), BATCH_id number);
create table s (batch_no varchar2(30), att varchar2(30));
I want to take values from attr_s and update attr_m against batch_no. How do I do that?
Here's my code, please help me fix this code, it doesn't work
update me
set a6 = (select att
from s where batch_no = (select he.batch_no
from he, s
where he.batch_no=s.batch_no))
error received:
single row subquery return multiple rows.
single row subquery return multiple rows
The update statement is applied to each individual row in ME. Therefore the assignment operation requires one scalar value to be returned from the subquery. Your subquery is returning multiple values, hence the error.
To fix this you need to further restrict the subquery so it returns one row for each row in ME. From your data model the only way to do this is with the BATCH_ID, like so:
update me
set a6 = (select att
from s where batch_no = (select he.batch_no
from he, s
where he.batch_no=s.batch_no
and he.batch_id = me.batch_id))
Such a solution will work providing that there is only one record in S which matches a given permutation of (batch_no, batch_id). As you have provided any sample data I can't verify that the above statement will actually solve your problem.

Procedure to remove duplicates in a table

Brief model overview:
I have a student and a course tables. As it's many to many relation there is also a junction table student_course (id_student, id_course), with unique constraint on both columns (composite).
The problem I want to solve:
On account of a mistake, there is no a unique constraint on the code column of the course table. It should as code column should uniquely identify a course. As a result there are two rows in the course table with the same value in the code column. I want to remove that duplicate, check that there is no other duplicates and add a unique constraint on the code column. Without loosing relations with student table.
My approach to solve the issue:
I have create a procedure that should do what I want.
CREATE OR REPLACE PROCEDURE REMOVE_COURSES
(
v_course_code IN VARCHAR2,
v_course_price IN VARCHAR2
)
AS
new_course_id NUMBER;
BEGIN
INSERT INTO course (CODE, PRICE) VALUES (v_course_code, v_course_price)
RETURNING ID INTO new_course_id;
FOR c_course_to_overwrite IN (SELECT *
FROM course
WHERE code = v_course_code AND id != new_course_id) LOOP
UPDATE student_course SET id_course = new_course_id WHERE id_course = c_course_to_overwrite.id;
DELETE FROM course WHERE id = c_course_to_overwrite.id;
END LOOP;
END REMOVE_COURSES;
/
Main problem I want to solve:
The procedure keeps giving me an error about unique constraint violation on student_course table. But I am really not sure how it's possible as I am using new_course_id, so there is no chance that in the junction table there are two rows with the same id_student, id_course. What do I need to fix ?
Miscellaneous:
I want to solve that issue using procedure only for learning purposes
EDITED:
CREATE TABLE student (
id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY,
name VARCHAR2(150) NOT NULL,
PRIMARY KEY (id)
);
ALTER TABLE student MODIFY ID
GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH LIMIT VALUE);
CREATE TABLE course (
id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY,
code VARCHAR2(255) NOT NULL,
PRIMARY KEY (id)
);
ALTER TABLE course MODIFY ID
GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH LIMIT VALUE);
CREATE TABLE student_course (
id_student NUMBER NOT NULL,
id_course NUMBER NOT NULL,
PRIMARY KEY (id_student, id_course),
CONSTRAINT student_fk FOREIGN KEY (id_student) REFERENCES student (id),
CONSTRAINT course_fk FOREIGN KEY (id_course) REFERENCES course (id)
);
insert into student (name) values ('John');
INSERT INTO course (ID, CODE) VALUES (1, 'C_13');
INSERT INTO course (ID, CODE) VALUES (2, 'C_13');
commit;
INSERT INTO STUDENT_COURSE (ID_STUDENT, ID_COURSE) VALUES (1, 1);
INSERT INTO STUDENT_COURSE (ID_STUDENT, ID_COURSE) VALUES (1, 2);
commit;
CALL REMOVE_COURSES('C_13');
[23000][1] ORA-00001: unique constraint (SYS_C0014983) violated ORA-06512: near "REMOVE_COURSES", line 8
Rather than removing one of the duplicate codes, you're creating a third course with the same code, and trying to move all students on either of the old courses onto the new one. The error suggests you have students who are already enrolled on both of the old courses.
Your cursor loop query is:
SELECT *
FROM course
WHERE code = v_course_code AND id != new_course_id
That will find all junction records for both old versions of the code, and the update then sets all of those junction records to the same new ID.
If there are any students listed against both old IDs for the code - which would be allowed by your composite unique key - then they will both be updated to the same new ID.
So say the courses you're looking at are [updated for your example code]:
ID CODE
-- ----
1 C_13
2 C_13
and you have junction records for a student for both courses, like:
ID_STUDENT ID_COURSE
---------- ---------
1 1
1 2
You are creating a new course:
ID CODE
-- ----
3 C_13
Your cursor loop looks for code = 'ABC' and ID != 3, which finds IDs 1 and 2. So in the first iteration of the loop up update the rows with ID 1, so now you have:
ID_STUDENT ID_COURSE
---------- ---------
1 3
1 2
Then in the second iteration you try to update the rows with ID 2, which would attempt to produce:
ID_STUDENT ID_COURSE
---------- ---------
1 3
1 3
which would break the unique constraint - hence the error.
You probably don't want to create a new course at all, but either way, you need to remove duplicate records from student_course - that is, rows which will become duplicates when updated. Basically you need to find students with entries for both existing course IDs, and delete either of them. If you don't care which this would do it:
delete from student_course sc1
where id_course in (
select id
from course
where code = 'C_13'
)
and exists (
select null
from student_course sc2
join course c on c.id = sc.id_course
where sc2.id_student = sc1.id_student
and sc2.id_course > sc1.id_course
and c.code = 'C_13'
);
but there are other (probably better) ways.
You then have the choice of updating all remaining junction records for both old IDs to your new ID; or to consolidate on one of the old IDs and remove the other.
(Your question implies you want to solve the overall task yourself, so I'll refrain from trying to provide a complete solution - this just hopefully helps you understand and resolve your main problem...)

Regarding query to find the records after a join on a particular condition

I have two table like as shown below, first one is employee having columns
empid empname empstaus
and second is table is bbb
eempid empdept empworty
both the table are joined in id basis
now I have to do a sort of filetering as in emp table staus can be a value or null so for a
employee whose value is null I need to check in other table bbb and print all the details only for emplyees whose staus is null in emp table please advise how to achieve this , i am using oracle as a database
If I understand correctly try
SELECT b.*
FROM bbb b JOIN employee e
ON b.eempid = e.empid
WHERE e.empstatus IS NULL
Here is SQLFiddle demo.

Update query resulting wrongly

I have table called company_emp. In that table I have 6 columns related to employees:
empid
ename
dob
doj, ...
I have another table called bday. In that I have only 2 columns; empid and dob.
I have this query:
select empid, dob
from company_emp
where dob like '01/05/2011'
It shows some list of employees.
In the same way I have queried with table bday it listed some employees.
Now I want to update the company_emp table for employees who have date '01/05/2011'.
I have tried a query like this:
update company_name a
set dob = (select dob from bday b
where b.empid=a.empid
and to_char(a.dob,'dd/mm/yyyy') = '01/05/2011'}
Then all the records in that row becoming null. How can I fix this query?
You're updating every row in the company_name/emp table.
You can fix that with a correlated subquery to make sure the row exists, or more efficiently by placing a primary or unique key on bday.empid and querying:
update (
select c.dob to_dob,
d.dob from_dob
from company_emp c join dob d on (c.empid = d.empid)
where d.dob = date '2011-05-01')
set to_dob = from_dob
Syntax not tested.

Oracle Sql Update with values using foreign key?

Table 1:
Name, x1-X2, fk1, fk2.
Table 2:
K1(parent for table 1),X
How to update table 1 whose second column x1-x2 depands on fk1,fk2 from Table 2
Table1:
a,1.0,1,2
b,-3.0,2,3
Table 2
1,4.0
2,5.0
3,2.0
With this setup:
CREATE TABLE table2 (k NUMBER PRIMARY KEY, x NUMBER);
CREATE TABLE table1 (
NAME VARCHAR2(10) PRIMARY KEY,
diff NUMBER,
fk1 NUMBER REFERENCES table2,
fk2 NUMBER REFERENCES table2);
the following update will "refresh" the colum table1.diff with the values of table2:
SQL> UPDATE (SELECT child.diff old_diff, parent2.x - parent1.x new_diff
2 FROM table1 child
3 JOIN table2 parent1 ON (child.fk1 = parent1.k)
4 JOIN table2 parent2 ON (child.fk2 = parent2.k))
5 SET old_diff = new_diff
6 WHERE old_diff != new_diff
7 OR (old_diff IS NULL AND new_diff IS NOT NULL)
8 OR (old_diff IS NOT NULL AND new_diff IS NULL);
Only the rows that need updating will be refreshed (thanks to the where clause).
Not quite sure I understand the question, referential integrity constraints do not prevent you from updating a table. If you need to do a lot of work in a transaction you can look into Deferred Constraints. Is there any chance you could clarify what you mean?
Not sure exactly what the problem is, maybe you need to rephrase the question a little.
In general, the foreign key constraint makes sure that there exists a corresponding row in the referenced table.
When you update a row with a foreign key, and try to set a value that does not point to such a master row, you will get an error, either immediately or when you commit (depending on when the constraint is enforced, it can be deferred).
Other than that, the update is no different than any other update.
Since the data in table 1 is dependent on the data in table 2 you won't be able to "Update" table 1 directly.
You will have to perform the update on table 2 and then recalculate table 1.
You could do it inside a transaction or perhaps a trigger on table 2.
Another option may be to have table one only hold the foreign keys and the name and then create a view that calculates the X1-X2 value.
EDIT
After looking at the sample data, I don't think you can unambiguously have table 2 be updates as a result of an update on table 1.
For example if you update the second column of table 1 to be 43, how would you know what values to set the specific rows of table 2 (it could be 40 and 3, 20 and 23, etc.)

Resources