CheckSum on SQL Server Record - sql

Is it a good idea to add checksum on each record in SQL Server (updated by application only) to prevent and check whether the record was updated manually using an external script? If not, what is the best approach for this?

Use triggers to prevent changes (roll them back). Or use them to audit changes (log the changes into a seperate table).
-- Add trigger to prevent data changes
CREATE TRIGGER [active].[myTriggerOfInfinitePower] ON [active].[mySpecialSparkleTable]
FOR INSERT, UPDATE, DELETE AS
BEGIN
-- Detect inserts
IF EXISTS (select * from inserted) AND NOT EXISTS (select * from deleted)
BEGIN
ROLLBACK TRANSACTION;
RAISERROR ('inserts are not allowed on that table', 15, 1);
RETURN;
END
-- Detect deletes
IF EXISTS (select * from deleted) AND NOT EXISTS (select * from inserted)
BEGIN
ROLLBACK TRANSACTION;
RAISERROR ('deletes are not allowed on that table', 15, 1);
RETURN;
END
-- Detect updates
IF EXISTS (select * from inserted) AND EXISTS (select * from deleted)
BEGIN
ROLLBACK TRANSACTION;
RAISERROR ('updates are not allowed on that table', 15, 1);
RETURN;
END
END;
GO
Source: use triggers to prevent changes
Regarding checksums:
Adding a checksum column (a) won't "prevent" a record change via SQL Script (etc.) and (b) someone running such a script could always update the checksum (in theory - unless you do some secret squirrel hashing).

Related

How to use multiple triggers?

DROP TRIGGER IF EXISTS N2Trigger
CREATE TRIGGER N2Trigger
ON dbo.Date
FOR INSERT, DELETE
AS
BEGIN
SELECT 'Inserted Datebase' as MESSAGE
SELECT 'Deleted Database' as MESSAGE
END
DELETE FROM dbo.[Date] WHERE ID = 1
Here is my code I just want when I use insert statement return 'Inserted Datebase' as MESSAGE
When I use delete statement return 'Deleted Database' as MESSAGE
The easiest way to check what action fired the trigger is to inspect the inserted and deleted pseudo-tables. If the trigger is only on DELETE/INSERT and not on update, then the logic is simply:
CREATE TRIGGER dbo.trFlarb ON dbo.flarb
FOR INSERT, DELETE
AS
BEGIN
IF EXISTS (SELECT 1 FROM inserted)
BEGIN
SELECT 'Inserted.';
END
IF EXISTS (SELECT 1 FROM deleted)
BEGIN
SELECT 'Deleted.';
END
END
Example db<>fiddle
Now, of course, Marc is right: triggers aren't for returning or printing output. This is just a demonstration that you can use those checks to then perform whatever logic you need to perform in the event of either action.
That said, if you have two distinctly different things you want to do depending on whether it's an insert or a delete, why not just create two separate triggers?
CREATE TRIGGER dbo.tr_I_Flarb ON dbo.flarb
FOR INSERT
AS
BEGIN
SELECT 'Inserted.';
END
GO
CREATE TRIGGER dbo.tr_D_Flarb ON dbo.flarb
FOR DELETE
AS
BEGIN
SELECT 'Deleted.';
END
GO
Note that SELECT will only "work" on your system if you haven't turned on the disallow results from triggers Server Configuration Option. Again, you should try to explain what you really want to do in the event of an insert or update, because the end goal can't be to print or return "Inserted" or "Deleted."

Trigger to ensure each new bid is higher than previous

I have a problem with my trigger in SQL Server.
This trigger checks if the new bidding is higher then the existing ones and if not raise an error:
ALTER TRIGGER [dbo].[trg_bod_validate_Bodbedrag]
ON [dbo].[bod]
FOR INSERT, UPDATE
AS
DECLARE #v_Voorwerp numeric(25);
DECLARE #v_Bodbedrag char(6);
DECLARE #v_Max_Bodbedrag char(6);
select #v_Voorwerp = i.voorwerp, #v_Bodbedrag = i.bodbedrag
from Inserted i;
SELECT #v_Max_Bodbedrag = max(CAST(bodbedrag AS INT))
FROM bod
WHERE voorwerp = #v_Voorwerp;
IF #v_Max_Bodbedrag <= #v_Bodbedrag
BEGIN
RAISERROR ('Bod moet hoger zijn dan gegeven bod.', 16, 1)
ROLLBACK TRANSACTION
END;
ELSE
PRINT 'Row Inserted';
Now I get this error Bid amount is less then maximum, that is not acceptable', even when I insert a bidding when there aren't any existing bids.
What could be the problem?
For your knowledge: Voorwerp: Product, Bodbedrag: Amount of bid
Points to note:
In SQL Server the Inserted (and Deleted) pseudo-tables can have from 0-N rows where N is the number of rows being inserted/updated/deleted. And this has to be handled in any trigger. However as soon as you switch into set-based thinking (instead of procedural) you find its a much simpler problem anyway.
Of course we can't (easily) discriminate between rows which are OK and those which aren't, we basically have to reject the entire insert/update even if its just one row which breaks the rules.
So the join finds the max(bodbedrag) for all products which exist in Inserted - excluding the row which is being inserted/updated - because as far as we are concerned the insert/update has already taken place and that data exists in our database - until we rollback if we choose to.
I've ignored your use of a char instead of a decimal. Ideally you would correct your datatypes, but you can continue to cast/convert if you wish. I'll leave that to you.
Note we use throw these days, not raiserror.
ALTER TRIGGER [dbo].[trg_bod_validate_Bodbedrag]
ON [dbo].[bod]
FOR INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
-- Find the max of for inserted voorwerp's excluding the currently inserted/updated
-- Is an update of existing bid even allowed
IF EXISTS (
SELECT 1
FROM Inserted I
LEFT JOIN (
SELECT MAX(bodbedrag) bodbedrag
FROM dbo.dob D
INNER JOIN Inserted I ON I.voorwerp = D.voorwerp and I.ID <> D.ID
) D
WHERE I.bodbedrag < coalesce(D.bodbedrag,0)
) BEGIN
-- THROW should be used now, not RAISERROR
-- RAISERROR ('Bod moet hoger zijn dan gegeven bod.', 16, 1);
THROW 51000, 'Bod moet hoger zijn dan gegeven bod.', 1;
ROLLBACK TRANSACTION;
END; ELSE BEGIN
PRINT 'Row Inserted';
END;
END

Stored procedure for referential integrity between two tables in different database?

For two tables A and B, I'd like to implement referential integrity such that in tables A a foreign key's value must present in table B, while in table B a primary key can only be deleted or modified if that value does not present in table A. My requirement is that I'd like to have table A, and B as variable, and apply the procedure to any arbitrary instances of tables. That is,
sp_referential_integrity_across_databases(A, B)
I have figured out how to do the referential integrity as triggers for a pair of particular tables. I wonder if it's feasible to write such stored procedure to save future effort?
My environment is Microsoft SQL Server 2017. The more portable the solution, the better.
Here are my crafted procedures:
The triggers on table "A" for insert and update:
USE DWPractice
IF OBJECT_ID ('dbo.trgCheckCustomer_Cat_Id_Customer_D', 'TR') IS NOT NULL
DROP Trigger trgCheckCustomer_Cat_Id_Customer_D;
GO
CREATE TRIGGER trgCheckCustomer_Cat_Id_Customer_D
ON Customer_D
AFTER INSERT, UPDATE
AS
IF NOT EXISTS
(
SELECT Customer_Cat_Id
FROM inserted
WHERE Customer_Cat_Id IN (SELECT Customer_Cat_Id FROM [OtherDW].[dbo].[Customer_Cat_D])
)
BEGIN
RAISERROR('Lookup Value Not Found -- Inerst Failed', 16, 1);
ROLLBACK TRANSACTION;
END;
The trigger on table "B" for delete and update:
USE OtherDW
IF OBJECT_ID ('dbo.trgCheckCustomer_Cat_Id_Customer_Cat_D', 'TR') IS NOT NULL
DROP Trigger trgCheckCustomer_Cat_Id_Customer_Cat_D;
GO
CREATE TRIGGER trgCheckCustomer_Cat_Id_Customer_Cat_D
ON Customer_Cat_D
AFTER DELETE, UPDATE
AS
Begin
IF EXISTS
(
SELECT Customer_Cat_Id
FROM deleted
WHERE Customer_Cat_Id IN (SELECT Customer_Cat_Id FROM [DWPractice].[dbo].[Customer_D])
)
BEGIN
RAISERROR('Lookup Value Found -- Delete Failed', 16, 1);
ROLLBACK TRANSACTION;
END;
-- It seems that the following for the case of update is not needed
-- The above clauses would get executed even for the case of update.
-- IF EXISTS
-- (
-- SELECT Customer_Cat_Id
-- FROM inserted
-- WHERE Customer_Cat_Id IN (SELECT Customer_Cat_Id FROM [DWPractice].[dbo].[Customer_D])
-- )
-- BEGIN
-- RAISERROR('Lookup Value Found -- Update Failed', 16, 1);
-- ROLLBACK TRANSACTION;
-- END;
End;
If stored procedure is not the best practice, then what is the best practice? It seems to me, there's much boiler-plate code, with only database name and table name are variables.
The logic in your (first) trigger is not correct. If you have multiple rows in inserted, then only one has to match. Instead, you want:
CREATE TRIGGER trgCheckCustomer_Cat_Id_Customer_D ON Customer_D AFTER INSERT, UPDATE
AS BEGIN
IF EXISTS (SELECT 1
FROM inserted i LEFT JOIN
[OtherDW].[dbo].[Customer_Cat_D] d
ON i.Customer_Cat_Id = d.Customer_Cat_Id
WHERE d.Customer_Cat_Id IS NULL
)
BEGIN
RAISERROR('Lookup Value Not Found -- Insert Failed', 16, 1);
ROLLBACK TRANSACTION;
END;
END; -- trigger

Is it possible to rollback DELETE, DROP and TRUNCATE?

We can rollback a delete query but not so for truncate and drop. When I execute queries then successfully done with rollback in delete, drop & truncate.
We can rollback the data in conditions of Delete, Truncate & Drop.
But must be used Begin Transaction before executing query Delete, Drop & Truncate.
Here is example:
Create Database Ankit
Create Table Tbl_Ankit(Name varchar(11))
insert into tbl_ankit(name) values('ankit');
insert into tbl_ankit(name) values('ankur');
insert into tbl_ankit(name) values('arti');
Select * From Tbl_Ankit
/*======================For Delete==================*/
Begin Transaction
Delete From Tbl_Ankit where Name='ankit'
Rollback
Select * From Tbl_Ankit
/*======================For Truncate==================*/
Begin Transaction
Truncate Table Tbl_Ankit
Rollback
Select * From Tbl_Ankit
/*======================For Drop==================*/
Begin Transaction
Drop Table Tbl_Ankit
Rollback
Select * From Tbl_Ankit
For MySql:
13.3.2 Statements That Cannot Be Rolled Back
Some statements cannot be rolled back. In general, these include data definition language (DDL) statements, such as those that create or drop databases, those that create, drop, or alter tables or stored routines.
You should design your transactions not to include such statements. If you issue a statement early in a transaction that cannot be rolled back, and then another statement later fails, the full effect of the transaction cannot be rolled back in such cases by issuing a ROLLBACK statement.
https://dev.mysql.com/doc/refman/8.0/en/cannot-roll-back.html
All of the above 3 transactions can be rolled back because all of them generate detail log. See this SO answer for more information. And this blog for detailed explanation with examples.
DELETE(DML)-------------------
Used to delete the tuples(row) of the table and have facility to DELETE ONE OR
MULTIPLE ROW USING WHERE CLAUSE
delete EX ->
 delete from emp ;
it will delete all the row of table
 delete from emp where id = 2;
it will delete one row
----------------------Drop(DDL)-------------------
This command will delete the whole structure of table with data so that's why it
is very important or crucial command and the access is to this command is only
to database administrator (DBA)
dropEX -> DROP EMP1;
it will vanish whole table with structure
-------------------TRUNCATE(DDL)----------------
Used to delete all rows (tuple) of the table.
truncate EX -> TRUNCATE EMP1;
----------------------QUESTIONS-------------------
QUES 1: IF WE CAN DELETE ALL ROWS FROM BOTH 'DELETE' COMMAND AND
'TRUNCATE' COMMAND THEN WHAT IS THE DIFFERENCE
DELETE AND TRUNCATE DIFFERENCE
DELETE command is DML command and TRUNCATE is DDL
TRUNCTE command will delete all rows because we can not use where clause
with truncate but WHERE clause can be applied to DELETE command and can
delete one or more rows through where condition
QUES 2: WHY TRUNCATE IS DDL ALTHOUGH IT IS WORKING ON DATA AND
DELETE IS DML IT IS ALSO WORKING ON DATA?
ANSWER:
 When TRUNCATE Command deletes all the row then we can never ROLLBACK
our deleted data because in truncate COMMIT is called implicitly
 But in the case of delete the log(backend file) is created from where you can get
your data ROLLBACK if deleted by mistake
 REMEMBER: In DELETE case ROLLBACK can be done only before the COMMIT

Sql Server Triggers. How does the trigger checks for Update Command?

I have a trigger for auditing purchase and sales table. The trigger is on INSERT and DELETE. To check if its "Insert", the condition is
IF (SELECT COUNT(*) FROM INSERTED) > 0
BEGIN
END
How do I check if its an Update command inside the Trigger?
Arun
The "tables" that are in play in an update trigger are still called inserted and deleted, and the old values of the rows are in the deleted table, and the new values are in the inserted table.
You can use logic along these lines to detect whether you are in a insert, update or a delete:
CREATE TRIGGER MyTrigger
ON MyTable
AFTER INSERT,DELETE,UPDATE -- you need to add the "update" here,
-- in order to catch updates as well
AS
BEGIN
Declare #insertedCount int
Declare #deletedCount int
select #insertedCount = COUNT(*) from inserted
select #deletedCount = COUNT(*) from deleted
if (#insertedCount != 0) -- we have some new values
if (#deletedCount = 0) -- do we have old values?
print 'Inserting'
else
print 'Updating'
else
print 'Deleting'
END
An INSERT,DELETE trigger will not fire for updates.
.........................
CREATE an INSERT, UPDATE, DELETE all in one table
IF ##ROWCOUNT = 0
BEGIN
RETURN;
END;
IF EXISTS(SELECT * FROM inserted)
BEGIN
IF EXISTS(SELECT * FROM deleted)
BEGIN
-- for UPDATE
END
ELSE
BEGIN
-- for INSERT
END
END
ELSE
BEGIN
-- for DELETE
END;
Once you've updated your trigger (as Mitch says) to also apply to UPDATEs, you need to check for updates first:
IF EXISTS(select * from inserted) and EXISTS(select * from deleted)
BEGIN
--Update
END
Note that I've switch to EXISTS rather than COUNT(*). Exists more properly describes what we want to establish - that rows exist in the table. We don't care how many rows are in there.
I would like to thank "SWeko" for the clue.
Now, this what I did and the Trigger worked.
CREATE TRIGGER dbo.Sample
ON TableName
FOR INSERT, UPDATE, DELETE
AS
IF (SELECT COUNT(*) FROM INSERTED) > 0
BEGIN
IF (SELECT COUNT(*) FROM DELETED) > 0
BEGIN
-- UPDATE (TABLE).
END
ELSE
BEGIN
-- INSERT (TABLE).
END
END
ELSE
-- DELETE (TABLE).
BEGIN
END