The following query use for check duplicate data in table then update or insert row - sql

I have the following query use for check duplicate data in table. If match data then update row else insert new row. In my case I have already one matched row in att_log table where emp_id=19.1.0121 and where mp_pk_id='32' AND att_date='2021-10-01', so result should be SET holiday=H in the matched row. But the DECLARE statement run without error and in console show affected row:1, but no change occur in data base, holiday not set to "H".
DECLARE c_emp_id att_log.emp_id%type;
BEGIN
SELECT emp_id
INTO c_emp_id
FROM att_log
WHERE emp_id='19.1.0121'
AND emp_pk_id='32'
AND att_date='2021-10-01' ;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
UPDATE att_log
SET holiday = 'H',
updated_at = '2021-08-22'
WHERE emp_id='19.1.0121'
AND att_date='2021-10-01';
WHEN NO_DATA_FOUND THEN
INSERT INTO att_log (emp_id, emp_pk_id, att_date, holiday,login_time, logout_time)
VALUES ('19.1.0121', '32', '2021-10-01','H','','');
COMMIT WORK;
END;
If I run the query separately without DECLARE statement then data row change happen, but with the above DECLARE statement no change happen in data row in the ORACLE table. What is my fault! Sorry, I am new to ORACLE, and also sorry for poor English.

A MERGE operation can INSERT or UPDATE (and also DELETE) depending on whether the row exists or not.
Here's a working test case:
Test case / fiddle
Example of MERGE:
CREATE TABLE logs (
id NUMBER GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL
, text VARCHAR2(20) UNIQUE
, q int DEFAULT 1
);
INSERT INTO logs (text) VALUES ('A');
INSERT INTO logs (text) VALUES ('B');
INSERT INTO logs (text) VALUES ('C');
MERGE INTO logs USING (SELECT 'B' AS text FROM dual) cte ON (cte.text = logs.text)
WHEN MATCHED THEN UPDATE SET logs.q = logs.q + 1
WHEN NOT MATCHED THEN INSERT (logs.text)
VALUES (cte.text)
;
Result:
and now we can do this for several existing rows and new rows at once:
MERGE INTO logs USING (
SELECT text FROM logs WHERE text > 'A' UNION
SELECT 'Z' FROM dual
) cte ON (cte.text = logs.text)
WHEN MATCHED THEN UPDATE SET logs.q = logs.q + 1
WHEN NOT MATCHED THEN INSERT (logs.text)
VALUES (cte.text)
;
New Result:
This example will either INSERT a new row when the rows in cte do not exist in logs, and UPDATE any existing rows in logs when matches are found, by incrementing q.

Related

How to copy a column value to other column of same database table whenever a new record is inserted?

I have the following database table:
Book {Id(primary Key), Name, TotalNoOfCopies, NoOfCopiesAvailable}
I want that whenever a new record/row is inserted the column NoOfCopiesAvailabe contains the same value as in column TotalNoOfCopies.I tried using the following successful trigger:
for insert
as declare #id nvarchar(10),#name varchar(50),#author varchar(40),#totalNoOfCopies tinyint, #availableNoOfCOpies tinyint;
select #id=i.Id from inserted i;
select #name=i.Name from inserted i;
select #author=i.Author from inserted i;
select #totalNoOfCopies=i.TotalNoOfCopies from inserted i;
select #availableNoOfCopies=i.TotalNoOfCopies from inserted i;
delete from Book where Id=#id;
insert into Book values(#id,#name,#author,#totalNoOfCopies,#availableNoOfCOpies);
But it performs 3 transactions:
1.For inserting a new record
2.For deleting that new record3.For inserting the new record that has NoOfCopiesAvailable=TotalNoOfCopies
For example:
Insert into Book values('123','Book','Author',5,null)
Messages:
1 row(s) affected
1 row(s) affected
1 row(s) affectedI was wondering if there is any way of doing it in a single transaction?
Do you need an INSTEAD OF INSERT trigger:
CREATE TRIGGER Book_i ON Book
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO Book (
Id,
Name,
TotalNoOfCopies,
NoOfCopiesAvailable
) SELECT
Id,
Name,
TotalNoOfCopies,
TotalNoOfCopies
) FROM
INSERTED
END
GO
It is clear an elegant.
I would rather fix the table design and have NumberOfCopiesSold instead, which can be 0 by default.

How to apply Update if an item exists and Insert otherwise

How to create a procedure that goes from the top of the table and compares the value to null
- If a match is found, insert an element in this position.
- If not, the element is inserted into a new row
I need to correct a second row which contains null values in 4 last columns regardless of values in the Id and PropertyId columns
Here is a screenshot of my DB
Here is a samples of data:
Now it works so, which is not suitable to me, instead it should update the row with null values like on the last screenshot
But the next entry should overwrite the value of NULL for Item, ItemId, InstanceId and Instance
Write a stored procedure like:
create procedure INSERT_OR_UPDATE as
begin
if exists ( select * from Numerations where <your condition> )
begin
update Numerations set < ... > where < ... >
end
else
begin
insert into Numerations values <...>
end
end
You have to check the syntax because I cannot test my code right now.

I need insert 2 records in differents tables,

I need to insert 2 records into 2 different tables. The problem is that the two records will be with the same Id.
For example :
I have my Mannto table, with its IdMan and oters fields. I also have my Service table, with its IdServ.
What can I do to make this one equal? I am using Postgre. The Id of the Mannto table is serial and I need to use that one as a Foreign key in the Service table
I tried the following, but it does not work:
Insert into Mannto ( idMan, field 1 field2 ...etc)
values ( default, 'f1', 'f2'...etc)
Insert into Service ( idServ, fkMannto, field1...etc)
values (default, (in this part I need the same ManntoId called idMan), 'f1')
Thank you for any help you can provide!
INSERT INTO Mannto ( field1, field2 ...etc) VALUES ( 'f1', 'f2'...etc)
RETURNING idMan;
http://www.postgresql.org/docs/current/static/sql-insert.html
When field idMan uses a sequence to create a new value, you can refer to this value in the next INSERT using CURRVAL():
BEGIN; -- start transaction
INSERT INTO Mannto ( idMan, field 1 field2 ...etc)
VALUES ( default, 'f1', 'f2'...etc);
INSERT INTO Service ( idServ, fkMannto, field1...etc)
VALUES (default, currval('name_of_the_sequence') , 'f1');
COMMIT; -- commit both inserts
Maybe it's not the best solution but you could use a trigger.
CREATE TRIGGER MannToTrigger
AFTER INSERT OR UPDATE OR DELETE ON MannTo
FOR EACH ROW EXECUTE PROCEDURE insertService();
Fetch your MannTo inserted last code and throw the procedure after each insert.

Insert into a temporary table and update another table in one SQL query (Oracle)

Here's what I'm trying to do:
1) Insert into a temp table some values from an original table
INSERT INTO temp_table SELECT id FROM original WHERE status='t'
2) Update the original table
UPDATE original SET valid='t' WHERE status='t'
3) Select based on a join between the two tables
SELECT * FROM original WHERE temp_table.id = original.id
Is there a way to combine steps 1 and 2?
You can combine the steps by doing the update in PL/SQL and using the RETURNING clause to get the updated ids into a PL/SQL table.
EDIT:
If you still need to do the final query, you can still use this method to insert into the temp_table; although depending on what that last query is for, there may be other ways of achieving what you want. To illustrate:
DECLARE
id_table_t IS TABLE OF original.id%TYPE INDEX BY PLS_INTEGER;
id_table id_table_t;
BEGIN
UPDATE original SET valid='t' WHERE status='t'
RETURNING id INTO id_table;
FORALL i IN 1..id_table.COUNT
INSERT INTO temp_table
VALUES (id_table(i));
END;
/
SELECT * FROM original WHERE temp_table.id = original.id;
No, DML statements can not be mixed.
There's a MERGE statement, but it's only for operations on a single table.
Maybe create a TRIGGER wich fires after inserting into a temp_table and updates the original
Create a cursor holding the values from insert and then loop through the cursor updating the table. No need to create temp table in the first place.
You can combine steps 1 and 2 using a MERGE statement and DML error logging. Select twice as many rows, update half of them, and force the other half to fail and then be inserted into an error log that you can use as your temporary table.
The solution below assumes that you have a primary key constraint on ID, but there are other ways you could force a failure.
Although I think this is pretty cool, I would recommend you not use it. It looks very weird, has some strange issues (the inserts into TEMP_TABLE are auto-committed), and is probably very slow.
--Create ORIGINAL table for testing.
--Primary key will be intentionally violated later.
create table original (id number, status varchar2(10), valid varchar2(10)
,primary key (id));
--Create TEMP_TABLE as error log. There will be some extra columns generated.
begin
dbms_errlog.create_error_log(dml_table_name => 'ORIGINAL'
,err_log_table_name => 'TEMP_TABLE');
end;
/
--Test data
insert into original values(1, 't', null);
insert into original values(2, 't', null);
insert into original values(3, 's', null);
commit;
--Update rows in ORIGINAL and also insert those updated rows to TEMP_TABLE.
merge into original original1
using
(
--Duplicate the rows. Only choose rows with the relevant status.
select id, status, valid, rownumber
from original
cross join
(select 1 rownumber from dual union all select 2 rownumber from dual)
where status = 't'
) original2
on (original1.id = original2.id and original2.rownumber = 1)
--Only math half the rows, those with rownumber = 1.
when matched then update set valid = 't'
--The other half will be inserted. Inserting ID causes a PK error and will
--insert the data into the error table, TEMP_TABLE.
when not matched then insert(original1.id, original1.status, original1.valid)
values(original2.id, original2.status, original2.valid)
log errors into temp_table reject limit 999999999;
--Expected: ORIGINAL rows 1 and 2 have VALID = 't'.
--TEMP_TABLE has the two original values for ID 1 and 2.
select * from original;
select * from temp_table;

SQL Insert trigger to update INSERTED table values

I want to create an Insert trigger that updates values on all the inserted rows if they're null, the new values should be taken from a different table, according to another column in the inserted table.
I tried:
UPDATE INSERTED
SET TheColumnToBeUpdated =
(
SELECT TheValueCol FROM AnotherTable.ValueCol
WHERE AnotherTable.ValudCol1 = INSERTED.ValueCol1
)
WHERE ValueCol IS NULL
But I get this error:
Msg 286, Level 16, State 1, Procedure ThisTable_INSERT, Line 15
The logical tables INSERTED and DELETED cannot be updated.
How should I do that?
You need to update the destination table, not the logical table. You join with the logical table, though, to figure out which rows to update:
UPDATE YourTable
SET TheColumnToBeUpdated =
(
SELECT TheValueCol FROM AnotherTable.ValueCol
WHERE AnotherTable.ValudCol1 = INSERTED.ValueCol1
)
FROM YourTable Y
JOIN Inserted I ON Y.Key = I.Key
WHERE I.ValueCol IS NULL
You could change the trigger to an INSTEAD OF INSERT. This will let you check the incoming values and, if needed replace them with the values from your other table.
CREATE TRIGGER CoolTrigger
ON MyAwesomeTable
INSTEAD OF INSERT
AS
BEGIN
INSERT MyAwesomeTable (TheValueCol)
SELECT ISNULL(INSERTED.TheValueCol, AnotherTable.TheValueCol) AS TheValueCol
FROM INSERTED
JOIN AnotherTable ON INSERTED.ValueCol1 = AnotherTable.ValueCol1
END
NOTE: INSTEAD OF triggers do NOT cause recursion.
insert into output
(SELECT t1.ts - INTERVAL (SECOND(t1.ts)%10) SECOND,
t1.ts - INTERVAL (SECOND(t1.ts)%10) SECOND + INTERVAL 10 SECOND ,sum(t1.data),
FROM (select * from input
where unix_timestamp(ts) >= unix_timestamp('2000-01-01 00:00:10')
and unix_timestamp(ts) < unix_timestamp('2000-01-01 00:01:20')
)
as t1
GROUP BY UNIX_TIMESTAMP(t1.ts) DIV 10 );
This is where my output table is coming from.
So the insertion is not by values.
Im so sorry but I can't access my account from here (office),