I have got this update script updating certain columns:
update oppar
set oppar_run_mode = 0,
oppar_run_time = 0,
oppar_interval_ind = 'N' ,
oppar_destination = '',
oppar_run_date ='',
oppar_run_interval=''
where ( oppar_job_name, oppar_job_rec )
in
( ('CSCLM' , 'XYZ')
, ('ARCLEVEXT' , 'LMN'));
But there are cases where there is no record in the table oppar where the column
oppar_job_rec is XYZ or LMN.
Now I need to verify the existence of oppar_job_name=CSCLM
then if that exists.
I need to check the existence of the Job rec coresponding to CSCLM i.e oppar_job_rec=XYZ
and if it does not exists I need to add a new record with these details.
oppar_job_name=CSCLM
oppar_job_rec=XYZ
oppar_run_mode = 0
oppar_run_time = 0
oppar_interval_ind = 'N'
oppar_destination = ''
oppar_run_date =''
oppar_run_interval=''
If it exists then I need to update that row.
Please help and tell me if you need more info.
But how do I perform the checking if it could be done and I need to do this on about 100 records with different values for oppar_job_rec .
Oracle 9i Enterprise Edition release 9.2.8.0 - 64 bit Production
You can use a SQL Merge statement: http://psoug.org/reference/merge.html
Here's some example code:
Instead of hardcoding the job_name and job_rec, build a table (if they aren't already in some table):
CREATE TABLE oppar_jobs (oppar_job_name VARCHAR2(200),
oppar_job_rec VARCHAR2(200));
INSERT INTO oppar_jobs (oppar_job_name,oppar_job_rec)
VALUES ('CSCLM','XYZ');
INSERT INTO oppar_jobs (oppar_job_name,oppar_job_rec)
VALUES ('ARCLEVEXT','LMN');
Then you can run a MERGE as follows:
MERGE
INTO oppar
USING oppar_jobs
ON ( oppar_jobs.oppar_job_name = oppar.oppar_job_name
AND oppar_jobs.oppar_job_rec = oppar.oppar_job_rec)
WHEN MATCHED
THEN
UPDATE
SET oppar_run_mode = 0,
oppar_run_time = 0,
oppar_interval_ind = 'N' ,
oppar_destination = '',
oppar_run_date ='',
oppar_run_interval=''
WHEN NOT MATCHED
THEN
INSERT ( oppar_job_name,
oppar_job_rec,
oppar_run_mode,
oppar_run_time,
oppar_interval_ind,
oppar_destination,
oppar_run_date,
oppar_run_interval)
VALUES ( oppar_jobs.oppar_job_name,
oppar_jobs.oppar_job_rec,
0,
0,
'N',
'',
'',
'');
As you're using 9i merge is not an option; So, you have a number of options, 2 that involve PL?SQL.
Option 1: update then insert
If you don't care about errors occurring you can just run your update then run your insert. The update may do nothing and the insert may cause a primary key violation but at least you know everything has been done. If you do this each insert would have to be done separately.
Option 2: update then insert with error catching
Using PL/SQL you could do something like the following,
update my_table
set <col1> = :col1
where <blah>
if SQL%ROWCOUNT = 0 then
insert into my_table
values < my values >
elsif SQL%ROWCOUNT = 1 then
insert less...
end if;
Option 3: insert then update with error catching
insert into my_table
values < my values >
exception when dup_val_on_index then
update my_table
set <col1> : :col1
where <blah>
Related
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.
I want to add it - if exists update else insert!
so that insert is working but i want to only insert new value and if thing already here i want to update it. my code here only inserts data no metter what
BEGIN
DECLARE das int;
SELECT dasaxeleba.id INTO das FROM dasaxeleba WHERE dasaxeleba.barcode = NEW.barcode;
INSERT INTO sawyobi(das_id,raodenoba,tvitgirebuleba,gasayidi1,gasayidi2)
VALUES(das,new.raodenoba,new.fasi,new.gasayidi1,new.gasayidi2);
END
if you are using oracle then you can use something like that:
merge into sawyobi tgt
using (select id
from dasaxeleba
where barcode = NEW.barcode) src on (tgt.das_id = src.id)
when matched then update set
raodenoba = new.raodenoba,
tvitgirebuleba = new.fasi,
gasayidi1 = new.gasayidi1,
gasayidi2 = new.gasayidi2
when not matched then insert
(das_id,
raodenoba,
tvitgirebuleba,
gasayidi1,
gasayidi2)
values (src.id,
new.raodenoba,
new.fasi,
ew.gasayidi1,
new.gasayidi2)
not quite sure where your "new"-values come from... but i hope you get the idea
I have a table named Customer and the column in question is dbupddate. This column should contain the datetime of the query that resulted in the record bein inserted.
I have already made a default constraint to getdate():
CREATE TABLE [dbo].[customer]
(
[dbupddate] [DATETIME] NOT NULL
CONSTRAINT [DF_customer_dbupddate] DEFAULT (GETDATE()),...
but this does not prevent someone ofaccidentally entering an irrelevant value.
How can I ensure the column dbupddate has the insert datetime?
I guess the answer will contain a trigger. In this case, consider the following already existing trigger, that should not have its effects lost/modified in any way:
CREATE TRIGGER [dbo].[customer_ins_trig]
ON [dbo].[customer]
AFTER INSERT
AS
BEGIN
DELETE u
FROM transfer_customer_unprocessed u
WHERE EXISTS (SELECT 1 FROM inserted i WHERE i.code = u.code)
INSERT INTO transfer_customer_unprocessed (code, dbupddate)
SELECT code, dbupddate
FROM inserted
END
Maybe I could add some lines to that one to suit my needs? Or maybe create another one?
In the procedure which is inserting the data, just don't provide a variable for that column. Granted someone could open SSMS if they have the rights and update it, but you could restrict this with access too.
Additionally, you may want to look into rowversion if this is part of a larger initiative to track changes.
Here's a trigger that does what you want, I think. Note that the user cannot control content going into InsertDate.
This is a reasonable approach for keeping "last updated" info for your data. However, #scsimon if you are doing this for other reasons, ROWVERSION is worth exploring, does not require a trigger, and will be much more performant.
DROP TABLE IF EXISTS Test;
GO
CREATE TABLE Test (
Id INT NOT NULL ,
Content NVARCHAR(MAX) NOT NULL ,
InsertDate DATETIME NULL
);
GO
CREATE TRIGGER TR_Test
ON Test
AFTER INSERT, UPDATE
AS BEGIN
UPDATE t SET t.InsertDate = GETDATE() FROM Test t INNER JOIN inserted i ON i.Id = t.Id;
END;
GO
INSERT Test VALUES (1, '1', NULL), (2, '2', NULL), (3, '3', NULL);
SELECT * FROM Test;
GO
UPDATE Test SET Id = 4, Content = 4 WHERE Id = 1;
UPDATE Test SET Id = 5, Content = 5, InsertDate = NULL WHERE Id = 2;
SELECT * FROM Test;
GO
I have a table with 5 columns:
username (varchar)
password (int)
access (bit)
information (varchar)
image (varchar)
I want to prevent user from inserting to 2 columns information and image if access = true.
Is there anyway to do this using insert trigger ? Any help would be great.
With an INSTEAD OF INSERT trigger, you could easily "filter out" unwanted information, e.g. you could insert an empty string (or something else) in case that access is set to 1:
CREATE TRIGGER InsteadTrigger on dbo.YourTableNameHere
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO dbo.YourTableNameHere(username, password, access, information, image)
SELECT
username, password, access,
CASE access
WHEN 1 THEN '' ELSE i.information END,
CASE access
WHEN 1 THEN '' ELSE i.image END
FROM INSERTED i
END;
So if you insert a row with access = 0 - all the columns get stored as presented.
So if you try to insert a row with access = 1 - the columns information and image are being "cleared out" and an empty string is stored instead.
On SQL Server 2008 and newer, this insert here:
INSERT INTO dbo.YourTableNameHere(username, password,access,information, image)
VALUES ('test 1', 42, 0, 'testinfo 1', 'testimg 1'),
('test 2', 4711, 1, 'testinfo 2', 'testimg2')
SELECT * FROM dbo.YourTableNameHere
would result in two rows being saved to your database table, but the second row inserted will have empty information and image columns...
A simple CHECK constraint could be enough if you need this behavior at insert or update:
ALTER TABLE MySchema.MyTable
ADD CONSTRAINT CK_MyTable_BlockInformationImageWhenAccessIsTrue
CHECK( access = 1 AND information IS NULL AND image IS NULL OR access = 0 );
If you need this behavior only at insert moment then you could use this trigger:
CREATE TRIGGER trgI_MyTable_BlockInformationImageWhenAccessIsTrue
ON MySchema.MyTable
AFTER INSERT
AS
BEGIN
IF EXISTS
(
SELECT *
FROM inserted i
WHERE i.access = 1
AND (information IS NOT NULL OR image IS NOT NULL)
)
BEGIN
ROLLBACK TRANSACTION;
RAISERROR('Access denied', 16, 1);
END
END;
GO
I need to change a database to add a unique constraint on a table column, but the VARCHAR data in it is not unique.
How can I update those duplicate records so that each value is unique by adding a sequential number at the end of the existing data?
e.g. I would like to change 'name' to 'name1', 'name2', 'name3'
Here are 2 examples with using the MS SQL SERVER flavor of sql.
Setup Example:
create table test (id int identity primary key, val varchar(20) )
--id is a pk for the cursor so it can update using "where current of"
-- name a is not duplicated
-- name b is duplicated 3 times
-- name c is duplicated 2 times
insert test values('name a')
insert test values('name b')
insert test values('name c')
insert test values('name b')
insert test values('name b')
insert test values('name c')
Sql 2005\2008: ( Computed Table Expression )
begin tran; -- Computed table expressions require the statement prior to end with ;
with cte(val,row) as (
select val, row_number() over (partition by val order by val) row
--partiton is important. it resets the row_number on a new val
from test
where val in ( -- only return values that are duplicated
select val
from test
group by val
having count(val)>1
)
)
update cte set val = val + ltrim(str(row))
--ltrim(str(row)) = converting the int to a string and removing the padding from the str command.
select * from test
rollback
Sql 2000: (Cursor example)
begin tran
declare #row int, #last varchar(20), #current varchar(20)
set #last = ''
declare dupes cursor
for
select val
from test
where val in ( -- only return values that are duplicated
select val
from test
group by val
having count(val)>1
)
order by val
for update of val
open dupes
fetch next from dupes into #current
while ##fetch_status = 0
begin
--new set of dupes, like the partition by in the 2005 example
if #last != #current
set #row = 1
update test
--#last is being set during the update statement
set val = val + ltrim(str(#row)), #last = val
where current of dupes
set #row = #row + 1
fetch next from dupes into #current
end
close dupes
deallocate dupes
select * from test
rollback
I rolled back each of the updates because my script file contains both examples. This allowed me to test the functionality without resetting the rows on the table.
Open a cursor on the table, ordered by that column. Keep a previous value variable, initialized to null, and an index variable initialized to 0. If the current value = the previous value, increment the index and append the index to the field value. if the current value <> the previous value, reset the index to 0 and keep the field value as is. Set the previous value variable = the current value. Move on to the next row and repeat.
You could add another column to it... like
update mytable set mycolumn = concat(mycolumn, id)
where id in (<select duplicate records>);
replace id with whatever column makes mycolumn unique
What database are you using?
In Oracle there is a:
NOVALIDATE Validates changes but does not validate data previously existing in the table
Example:
ALTER TABLE <table_name> ENABLE NOVALIDATE UNIQUE;
If you are not using Oracle then check the SQL reference for your respective database.