Trigger which counts datediff - sql

Hello for hobby purposes i am trying to create a C# application with a MS SQL Server database which reassembles a hotel system. I am now trying to create a SQL trigger which calculates a datedifference. A reservation may not be longer than 6 weeks(42 days). However, my trigger goes off even when placing reservations which have a datedifference lower than 42 days, even if the difference is 1 day. so I am not sure what I am doing wrong.
My trigger:
create trigger trigger_reservation
on reservation
after update, insert
as
if exists
(
select reservationid, DATEDIFF(dd,Startdate,Enddate)
from reservation
group by reservationid, enddate, startdate
having DATEDIFF(dd,Startdate,Enddate) > 42
)
begin
raiserror('Error: Reservation may not be longer than 6 weeks',16, 1)
rollback transaction
end

Triggers are expensive to run and maintain. This type of check can be accomplished by a simple CHECK CONSTRAINT
CREATE TABLE reservation (
reservationid INT,
startdate DATE,
enddate DATE,
-- ...
CONSTRAINT reservation_dates_ck
CHECK(DATEDIFF(dd, startdate, enddate) < 43)
)
Here is a dbfiddle demo
And here's how you go about doing it with a trigger
CREATE TRIGGER trigger_reservation
ON reservation AFTER UPDATE, INSERT
AS
IF EXISTS (
SELECT *
FROM inserted
WHERE DATEDIFF(dd, startdate, enddate) > 42
)
BEGIN
RAISERROR ('Error: Reservation may not be longer than 6 weeks', 16, 1);
ROLLBACK TRANSACTION;
RETURN
END;
Here is a dbfiddle demo

Related

Making a foreign key reference only a subset of rows in the referenced table

I'm using MS SQL SERVER 2019.
In my database I have a table Events which stores events offered by a club
Events (Theme, Event_Date, Place, Event_Hour, Type)
Then I also have a table Reservations which stores reservations for those events:
Reservations (Id, Event_Theme) FK(Event_Theme) --> PK(Events.Theme)
My goal is to allow insertion of new rows in Reservations only if Event_Theme for that row is a theme of a future event (i.e one with Event_Date > CURRENT DATE): obviously reservations are not allowed for past events.
My attempt was the creation, inside Reservations table's creation, of the following check constraint:
CONSTRAINT CHK_Reservations_Event_Theme CHECK (Event_Theme IN (SELECT STRING_AGG(E.Theme, ',') WITHIN GROUP (ORDER BY E.Theme ASC)
FROM Events E
WHERE E.Event_Date>=(CAST( GETDATE() AS Date))))
But I then remembered that subqueries aren't supported for check constraints
What is an alternative way to implement the same logic?
As you can see in the example event number 2 is outdated, so a new row is not inserted.
Event number 1 can be inserted.
you can add more and more conditions to the WHERE for example if only 100 reservations can be made
CREATE TABLE Events (Theme int , Event_Date date, Place varchar(4), Event_Hour int, Type int)
INSERT INTO Events VALUES(1, DATEADD(month, 1, getdate()), 'A',1,1),
(2, DATEADD(day, -1, getdate()), 'B',2,2)
CREATE tABLE Reservations (Id int, Event_Theme int)
CREATE TRIGGER tr_Reservations ON Reservations
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO [Reservations](
[Id],
[Event_Theme])
SELECT Id,[Event_Theme]
FROM INSERTED
WHERE Event_Theme IN (SELECT STRING_AGG(E.Theme, ',') WITHIN GROUP (ORDER BY E.Theme ASC)
FROM [Events] E
WHERE E.Event_Date>=(CAST( GETDATE() AS Date)));
PRINT 'success.';
END
INSERT INTO Reservations VALUES (1,1),(2,2)
success.
3 rows affected
SELECT * FROM Reservations
Id | Event_Theme
-: | ----------:
1 | 1
db<>fiddle here
I found also a way without using a trigger.
I created the following stored function:
CREATE FUNCTION usf_CHECK_THEME(#EventTheme VARCHAR(45))
RETURNS VARCHAR(45)
BEGIN
DECLARE #Theme VARCHAR(45);
SET #Theme =NULL;
SET #Theme = (SELECT E.Theme
FROM Events E
WHERE E.Theme=#EventTheme AND E.Event_Date>(CAST(GETDATE() AS Date)));
RETURN #Theme;
END;
Theme is PK In table Events.
And then in the check constraint:
CONSTRAINT CHK_Reservations_Event_Theme CHECK (usf_CHECK_THEME(Event_Theme) IS NOT NULL)

SQL Assign datediff to table column in insert trigger

Table
CREATE TABLE CurrentApplication
(
StartDate datetime NOT NULL,
EndDate datetime NOT NULL,
NoOfDays integer,
StaffID integer NOT NULL,
AppStatus varchar(30) NOT NULL DEFAULT 'PENDING'
)
Insert Trigger
CREATE TRIGGER InsertNoOfDays ON CurrentApplication
AFTER INSERT
BEGIN
INSERT INTO CurrentApplication
NoOfDays AS Datediff(day, StartDate, EndDate)
END
I have NoOfDays column which should hold the DateDiff between StartDate and EndDate and the value should be inserted whenever a new record is inserted into the table. How do I write the trigger and do it? I've tried but my trigger doesn't work. Thanks!
One method is to use an INSTEAD OF trigger:
CREATE TRIGGER InsertNoOfDays ON CurrentApplication
INSTEAD OF INSERT AS
BEGIN
INSERT INTO CurrentApplication( . . ., NoOfDays)
SELECT . . .,
Datediff(day, StartDate, EndDate)
FROM inserted;
END;
One thing you can do is add a computed column instead of actual column as NoOfdays.
There are benefits and drawbacks in adding a computed column.
You don't have have a trigger and calculate it. Saves on writes.
This also means sometimes you cannot index on that column and reads may have an impact.
Here is the documentation from Microsoft on computed columns (documentation says it is only on SQL 2016 onwards):
Specify Computed Columns in Tables
But, here is sample in SQL 2014 that worked for me (I think it works in SQL 2012 also)
use tempdb
GO
/*
Wors in version:
Microsoft SQL Server 2014 - 12.0.2000.8 (X64)
Standard Edition (64-bit
*/
IF OBJECT_ID('tempdb.dbo.CurrentApplication') IS NULL
CREATE TABLE dbo.CurrentApplication
(
StartDate datetime NOT NULL,
EndDate datetime NOT NULL,
NoOfDays AS (datediff(dd, StartDate, EndDate)) PERSISTED,
StaffID integer NOT NULL,
)
INSERT INTO dbo.CurrentApplication(StartDate, EndDate, StaffId)
SELECT TOP 10
StartDate = DATEADD(dd, object_id, '1/1/2017')
,EndDate = DATEADD(dd, object_id*object_id, '1/1/2017')
,StaffId = object_id
FROM sys.objects
WHERE object_id < 300
order by Object_id
SELECT * FROM CurrentApplication
IF OBJECT_ID('tempdb.dbo.CurrentApplication') IS NOT NULL
DROP TABLE dbo.CurrentApplication
Since not eligible to add comments, adding as a new answer.
Adding to #Gordon Linoff's answer, just substituted column names instead of periods to see if that caused the error you getting. I didnt get that error. Not sure if there is anything with SQL 2012.
But, this works (in SQL 2014):
CREATE TRIGGER InsertNoOfDays ON dbo.CurrentApplication
INSTEAD OF INSERT AS
BEGIN
INSERT INTO CurrentApplication(StartDate, EndDate, NoOfDays, StaffID)
SELECT StartDate
,EndDate
,Datediff(dd, StartDate, EndDate)
,StaffId
FROM inserted as i;
END;

SQL Server 2012 Trigger

I have a small little thing with SQL that's been bothering me now for a while, let's say I have two tables (Customer and Loan). However, I want a trigger that's checking based on the Borrowertype attribute. I suppose with the second query after AND I need something to check whether the userID in Loans are the same as the one in Customer, but must be messing it up or I'm completely thinking this the wrong way.
CREATE TABLE Customer
(
userID int identity primary key,
Name varchar(20),
Borrowertype varchar(20)
);
CREATE TABLE Loan
(
Id int identity primary key,
userID int,
FOREIGN KEY (userID) REFERENCES Customer(userID)
);
IF OBJECT_ID ('Customer.maximum_books_per_user','TR') IS NOT NULL
DROP TRIGGER Customer.maximum_books_per_user;
GO
CREATE TRIGGER maximum_books_per_user ON Customer
AFTER INSERT
AS
IF (SELECT Borrowertype FROM Customer) = 'diffborrowertypehere'
AND (SELECT COUNT(*) FROM inserted AS i JOIN Customer AS c
ON ??? WHERE ???
) > 5
BEGIN
ROLLBACK TRANSACTION
RAISERROR('You have reached maximum allowed loans.', 16, 1)
END
GO
Your trigger needs to be on the Loan table, as that's where a row would be being inserted that could be rejected. Something like this:
EDIT: rewritten to handle inserts for multiple Customers at once
CREATE TRIGGER maximum_books_per_user ON Loan
FOR INSERT
AS
-- Fail if there are any customers that will have more than the maximum number of loans
IF EXISTS (
SELECT i.userID, COUNT(*)
FROM inserted i
JOIN Loan l
ON i.userID = l.userID
GROUP BY i.userID
HAVING COUNT(*) >= 5
)
BEGIN
ROLLBACK TRANSACTION
RAISERROR('You have reached maximum allowed loans.', 16, 1)
END

trigger assignment

I am tying to complete my assignment but i am facing some problem so i need your help
I have craeted a table name client contain 6 columns name(c_id,c_name,c_transfer,c_balance,day,time) now the assignment is that i have to create a trigger in which is day column contain Satauarday and Sunday it prints Sorry Bank closed and if time column contain 05:00 to 09:00 it will inserted but if the time is not reguarding the condition row cant inserted.
The codes are as follows:
create table client
(
c_id int identity primary key,
c_name varchar(50),
c_transfer money,
c_balance money,
[date] datetime,
[day] varchar(50),
)
alter trigger transactions
ON client
for Insert
as
begin
if(select top 1 [day] from client order by c_id desc)='Satuarday'
begin
print'Sorry Bank is closed today'
rollback;
commit;
end
if(select top 1 [day] from client order by c_id desc)= 'Sunday'
begin
print'Sorry Bank is closed today'
rollback;
commit;
end
if(select top 1 date from client order by c_id desc)not in (DATEDIFF(hh,'09:00','05:00'))
begin
print'Sorry Bank time is not this...'
rollback;
commit;
end
end
What you are looking for is the Inserted table. This table can be used inside a FOR INSERT trigger and contains the row that is being inserted.
GO
CREATE TRIGGER transactions
ON client
FOR INSERT AS
BEGIN
IF EXISTS(SELECT [day] FROM inserted
WHERE ([day] IN ('saturday', 'sunday'))
OR (DATEPART(hh,[date]) <= 5) OR DATEPART(hh,[date]) >= 9)
BEGIN
PRINT'Sorry Bank is closed at this time.'
ROLLBACK;
END;
END;

Create trigger prevent insert

I'm trying to execute the following trigger:
create trigger t23
on studies
after insert, update, delete
as
begin
REFERENCING NEW ROW NewStudent
FOR EACH ROW
WHEN (30 <= (SELECT SUM(credits) FROM Studies)
DELETE FROM NewStudent N
WHERE N.spnr = NewStudent.spnr
end
I'm trying to create a trigger which only inserts a student if the credits is < or == to '30'. The "Credits" is a type int.
I'm getting numerous errors trying to implement this trigger. I really have tried everything and i m out of options. Could someone who is expert in the field point me in the right direction?
The example "Using a DML AFTER trigger to enforce a business rule between the PurchaseOrderHeader and Vendor tables" in the CREATE TRIGGER MSDN documentation does exaclty what you're looking for:
USE AdventureWorks2008R2;
GO
IF OBJECT_ID ('Purchasing.LowCredit','TR') IS NOT NULL
DROP TRIGGER Purchasing.LowCredit;
GO
-- This trigger prevents a row from being inserted in the Purchasing.PurchaseOrderHeader table
-- when the credit rating of the specified vendor is set to 5 (below average).
CREATE TRIGGER Purchasing.LowCredit ON Purchasing.PurchaseOrderHeader
AFTER INSERT
AS
DECLARE #creditrating tinyint, #vendorid int;
IF EXISTS (SELECT *
FROM Purchasing.PurchaseOrderHeader p
JOIN inserted AS i
ON p.PurchaseOrderID = i.PurchaseOrderID
JOIN Purchasing.Vendor AS v
ON v.BusinessEntityID = p.VendorID
WHERE v.CreditRating = 5
)
BEGIN
RAISERROR ('This vendor''s credit rating is too low to accept new purchase orders.', 16, 1);
ROLLBACK TRANSACTION;
RETURN
END;
The key here is ROLLBACK TRANSACTION, just adapt the example to suit your need and you're done.
Edit: This should accomplish what you're looking for, but I have not tested it so your mileage may vary.
create trigger dbo.something after insert as
begin
if exists ( select * from inserted where sum(credits) > 30 )
begin
rollback transaction
raiserror ('some message', 16, 1)
end
end
Another edit, based on some assumptions (please note I wrote this script on the fly since I can't test it right now):
create table dbo.students
(
student_id int not null,
name varchar (50) not null
)
create table dbo.courses
(
course_id int not null,
name varchar (50) not null,
required_credits int not null
)
create table dbo.results
(
student_id int not null,
course_id int not null,
course_result int not null
)
create trigger dbo.check_student_results on dbo.results after insert as
(
declare #check int
select #check = count(*)
from inserted as a
join dbo.courses as b on b.course_id = a.course_id
where b.required_credits > a.course.result
if #check <> 0
begin
rollback transaction
raiserror('The student did not pass the course.', 16, 1)
end
)
This way when you insert records in the dbo.results table the constraint checks if the student has passed the course, and cancels the insertion if appropriate. However, it's better to check this things in the application layer.