how to disable some column using insert trigger? - sql

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

Related

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

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.

Microsoft SQL Server - default value provided by stored procedure

Is it possible to have a non-null column where the value is generated at insert by calling a stored procedure the parameters of which are values passed to insert into the row?
For example, I have table User:
| username | name | surname | id |
Insert looks like this:
INSERT INTO USER (username, name, surname)
VALUES ('myusername', 'myname', 'mysurname');
The id column is populated with an (integer) value retrieved by calling stored procedure mystoredproc with parameters myusername, myname, mysurname.
A further question is, would this stored procedure be called on each row, or can it be called in a grouped fashion. For example, I'd like my stored procedure to take the name and append a random integer to it so that that if I insert 100 users with the name 'David', they will get the same id and the stored procedure will be called only once. A bit of a bad example on the second point.
Good day,
Is it possible to have a non-null column where the value is generated at insert by calling a stored procedure
Option 1: please check if this work for you
Specify Default Value for the Column and use "NOT NULL"
create trigger on the table AFTER INSERT
Inside the trigger, you can use the virtual table "inserted" in order to get the inserted values.
Using these values (using the inserted table) you can update the column using the logic you need for all the rows at once
** there is no need to use external SP probably, but you can execute SP from trigger if needed
** All executed by a trigger is in the same transaction as the original query.
would this stored procedure be called on each row
NO! The trigger will be executed once for all rows you insert in the same statement. The inserted table includes all the rows which were inserted. In your update section (step 4) you can update all the rows which were inserted in once and no need to execute something for each row
** If you do use external SP which is executed from the trigger then you can pass it all the inserted table as one using Table-Valued Parameter
------------------- update ---------------
Here is a full example of using this logic:
drop table if exists T;
CREATE TABLE T (id int identity(2,2), c int NOT NULL default 1)
GO
CREATE TRIGGER tr ON T AFTER INSERT
AS BEGIN
SET NOCOUNT ON;
UPDATE T SET T.c = T2.C + 1
FROM inserted T2
INNER JOIN T T1 ON T1.id = T2.id
END
INSERT T(c) values (1) -- I insert the value 1 but the trigger will change it to 1+1=2
select * from T
GO
-- test multiple rows:
INSERT T(c) values (10),(20),(30),(40)
select * from T
GO
DECLARE #rc INT = 0,
#UserID INT = ABS(CHECKSUM(NEWID())) % 1000000 + 1;
WHILE #rc = 0
BEGIN
IF NOT EXISTS (SELECT 1 FROM dbo.Users WHERE UserId= #UserId)
BEGIN
INSERT dbo.Users(UserId) WHERE Username = #UserName SELECT #UserId;
SET #rc = 1;
END
ELSE
BEGIN
SELECT #UserId = ABS(CHECKSUM(NEWID())) % 1000000 + 1,
#rc = 0;
END
END

Trigger creation/modification to ensure field equals insertion date

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

AUDIT TRAIL OF TABLES

I want to do audit trail on specific table like
what inserted,updated,deleted in table and all this logs are save in one table
I am using sql server 2012 .
Can any one please help me with how to achieve this?
Please note - Use of cursor is restricted
create an after trigger on that table and insert the records into the log table .
create trigger <trigger_name> after insert/update/delete/ on
table <orig table>
begin
insert into the log tables ('all the fields that you require');
end
Try using CDC (Change Data Capture). A very helpful tool which will help to manage the Audit Trail
Read the article from MSDN
This can be achieve using Triggers. Trigger will make your DML operations slower, if large Insert, Delete and Update operations are happening on your table. If it's small table you can create TRIGGER like below, to log the rows to another table based on the action occurred.
You can make use of Inserted and Deleted magic tables which hold the rows which are being Inserted and Deleted inside a trigger.
There is another alternate if you need more control over auditing using CDC (Change Data Capture).
CREATE TABLE TrailTable
(
Id INT,
Name VARCHAR(100)
);
CREATE TABLE TrailTableLog
(
Id INT,
Name VARCHAR(100),
Action CHAR(3)
);
Insert Into TrailTable VALUES (1,'Vi');
Insert Into TrailTable VALUES (2,'Vr');
Insert Into TrailTable VALUES (3,'An');
Insert Into TrailTable VALUES (4,'Ma');
CREATE TRIGGER dbo.TRG_IDU_TrailTable
ON dbo.TrailTable
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Action as char(1);
SET #Action = (CASE WHEN EXISTS(SELECT * FROM INSERTED)
AND EXISTS(SELECT * FROM DELETED)
THEN 'U' -- Set Action to Updated.
WHEN EXISTS(SELECT * FROM INSERTED)
THEN 'I' -- Set Action to Insert.
WHEN EXISTS(SELECT * FROM DELETED)
THEN 'D' -- Set Action to Deleted.
ELSE NULL -- Skip. It may have been a "failed delete".
END)
IF(#Action = 'I')
BEGIN
INSERT INTO TRG_IDU_TrailTable (Id, Name, Action)
SELECT Id, Name, 'I' FROM INSERTED;
END
IF(#Action = 'D')
BEGIN
INSERT INTO TRG_IDU_TrailTable (Id, Name, Action)
SELECT Id, Name, 'D' FROM DELETED;
END
IF(#Action = 'U')
BEGIN
INSERT INTO TRG_IDU_TrailTable (Id, Name, Action)
SELECT Id, Name, 'U-D' FROM INSERTED; -- Records Deleted to Update
INSERT INTO TRG_IDU_TrailTable (Id, Name, Action)
SELECT Id, Name, 'U-I' FROM INSERTED; --Records Inserted to Update
END
END

Conditional adding of records to a table

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>