How do I validate these columns, do i need more complex triggers? - sql

I'm trying to validate the columns in the table ProjectDetails.TimeCards
create table ProjectDetails.TimeCards(
Time_Card_ID int identity (55,15),
Employee_ID int foreign key references HumanResources.Employees(Employee_ID),
Date_Issued date, --DateIssued should be greater than the current date and the project start date (Project start date is from another table).
Days_Worked int constraint chk_Days_Worked check(Days_Worked > '0'),
Project_ID int foreign key references ProjectDetails.Projects(Project_ID),
Billable_hours int constraint chk_Billable_Hours check (Billable_Hours > '0'),
Total_Cost money, -- should be automatically calculated by using the following formula: TotalCost=Billable Hours * BillingRate (billing rate is from another table)
Work_Code_ID int foreign key references ProjectDetails.WorkCodes(Work_Code_ID)
);
I tried building a trigger, but was only able to get the trigger to fire if the Date_Issued was less than the current date
CREATE TRIGGER dateissued
ON ProjectDetails.TimeCards
FOR INSERT
AS
DECLARE #ModifiedDate date
SELECT #ModifiedDate = Date_Issued FROM Inserted
IF (#ModifiedDate < getdate())
BEGIN
PRINT 'The modified date should be the current date. Hence, cannot insert.'
ROLLBACK TRANSACTION -- transaction is to be rolled back
END
i need the trigger to fire if the Date issued is less than the current date and also if date issued is less than the start_date. As for the billing rate calculation i'm lost there.
This is the other table
create table ProjectDetails.Projects(
Project_ID int identity (0100, 01) primary key, -- Primary key
Project_Name char (50) not null,
Start_Date date not null, -- the start date i'm referring to
End_Date date not null,
constraint CheckEndLaterThanStart check (End_Date > Start_Date),
Billing_Estimate money constraint chk_Billing_Estimate check (Billing_Estimate > '1000'),
Client_ID int Foreign Key references CustomerDetails.Clients(Client_ID)
);

I believe this provides the logic you are after. There are a couple of comments in the code for you:
CREATE TRIGGER trg_chk_Date_Issued ON ProjectDetails.TimeCards
FOR INSERT, UPDATE --I assume on need on UPDATE as well, as otherwise this won't validate
AS BEGIN
IF EXISTS(SELECT 1
FROM inserted i
JOIN ProjectDetails.Projects P ON i.Project_ID = P.Project_ID
WHERE i.Date_Issued < CONVERT(date,GETDATE())
OR i.Date_Issued < P.Start_Date) BEGIN
RAISERROR(N'Date Issued cannot be less than the current date or the Project Start Date.', 10,1); --Raised an error, rather than using PRINT
ROLLBACK;
END
END
Note that you may want a different trigger for UPDATE, as if you are updating the row, and Date_Issued has a value lower than the date of the UPDATE, the statement will fail.

Related

How do I validate these columns?

I'm working on a database and I need to validate some columns under the Payment schema.
Like if a credit card is not used for payments, CreditCardNumber, CardHoldersName, and CreditCardExpDate should be made NULL. If a credit card is used, the CreditCardExpDate value should be greater than the PaymentDate
PaymentDue can allow NULL but should not be greater than PaymentAmount
I've searched online but what I get are complex triggers and procedures which are not really helpful.
create table Payment.Payments(
Payment_ID int identity (200, 21),
Payment_Amount money constraint chk_Payment_Amount check (Payment_Amount >
'0'),
Payment_Date date, -- is to be greater than the end date which is on another table
Credit_Card_Number int,
Card_Holders_Name char (50),
Credit_Card_Expiry_Date date,
Project_ID int Foreign Key references ProjectDetails.Projects(Project_ID),
Payment_Due money -- should not be greater than Payment Amount but
can still accept null*
);
The notes show the current validation problem i'm having.
I created a trigger for the payment_date but i can only get it to fire when the inserted date is greater than the current date, i need it to fire if it is less than the end date(end date is on another table)
CREATE TRIGGER paymentdate
ON Payment.Payments
FOR INSERT
AS
DECLARE #ModifiedDate date
SELECT #ModifiedDate = Payment_Date FROM Inserted
IF (#ModifiedDate > getdate())
BEGIN
PRINT 'The modified date should be the current date. Hence, cannot insert.'
ROLLBACK TRANSACTION
END
I'm reading a lot between the lines here, but I think this is what you're after (Note I have used the dbo schema though):
USE Sandbox;
GO
CREATE TABLE dbo.Payments (
Payment_ID int identity(200, 21),
Payment_Amount money CONSTRAINT chk_Payment_Amount CHECK (Payment_Amount > '0'),
Payment_Date date,
Credit_Card_Number char(19), --note datatype change from int to char. See my comment below (copied from my comment)
Card_Holders_Name varchar (50), --note I've used varchar instead. Names aren't all 50 characters long
Credit_Card_Expiry_Date date,
--Project_ID int FOREIGN KEY REFERENCES ProjectDetails.Projects(Project_ID) --Commented out as I don't have this table
Payment_Due money CONSTRAINT chk_Payment_Due CHECK (Payment_Due > '0' OR Payment_Due IS NULL)
);
GO
--Credit Card format validation
ALTER TABLE dbo.Payments ADD CONSTRAINT ck_Credit_Card CHECK (Credit_Card_Number LIKE '[0-9][0-9][0-9][0-9] [0-9][0-9][0-9][0-9] [0-9][0-9][0-9][0-9] [0-9][0-9][0-9][0-9]' OR Credit_Card_Number IS NULL);
--Add card details must be there, or none.
ALTER TABLE dbo.Payments ADD CONSTRAINT ck_Card_Details CHECK ((Credit_Card_Number IS NULL AND Card_Holders_Name IS NULL AND Credit_Card_Expiry_Date IS NULL)
OR (Credit_Card_Number IS NOT NULL AND Card_Holders_Name IS NOT NULL AND Credit_Card_Expiry_Date IS NOT NULL))
GO
DROP TABLE dbo.Payments;
Comment made on the Card Number's datatype:
The datatype int for a credit card number is a bit of an oxymoron. The maximum value for an int is 2,147,483,647 and a card number is made up of 4 sets of 4 digit numbers (i.e. 9999 9999 9999 9999). Even as a number, that's far higher than the max value of an int. I'd suggest using a char(19) and making a constraint on the format as well.

How to update a column based on an amount of time in SQL

I want to create a stored procedure to update a column based on an amount of time. For example, to update the interest generated column every 15 days.
Here is my code. Please help.
create table Loan(
Loan_ID int not null primary key,
Loan_custID int not null foreign key references Customers(Cust_ID),
Loan_Amount int not null,
Loan_Interest int not null,
Loan_Date date not null unique,
)
Create table Interestgenerated(
IG_ID int not null primary key,
Loan_ID int not null foreign key references Loan1(Loan_ID),
Loan_Date date null foreign key references Loan1(Loan_Date),
IG_Amount int not null,
IG_Date datetime not null
)
create procedure InsertINtoInterestgenerated1
#PresentDate Datetime
as
set #PresentDate=getdate()
select Loan_ID from Loan
set IG_Date=Loan_Date
IG_Date=dateadd(day,15, IG_Date)
if #PresentDate=IG_Date
begin
update Interestgenerated1 set IG_Date = #PresentDate, IG_Amount=IG_Amount*0.15
end
Considering you want to automate the update of the value in column IG_Amount every 15 days,
you can schedule a job to run every 15 days at midnight like on the 1st and 16th of every month.
the below link might help you:
how to schedule a job for sql query to run daily?

DB2SQL Trying to create a trigger with function (Calculate days)

So for now, I created 2 tables, Booking and BookingDetails, I want to create a trigger when I key in the details of BookingDetails it will automatically update totaldays inside the Booking table. Below is my code:
BookingDetails:
create table BookingDetail (
BD_ID int primary key not null,
Date_In date,
Date_Out date,
BK_ID int,
Room_ID int,
foreign key(BK_ID) references Booking(BK_ID),
foreign key(Room_ID) references Room(Room_ID)
)
And also Booking
create table Booking (
BK_ID int primary key not null,
BK_Date Date,
BK_TotalDays int,
BK_PayStatus char(6),
Cus_ID int,
Emp_ID int,
foreign key(Cus_ID) references customer(Cus_ID),
foreign key(Emp_ID) references Employee(Emp_ID)
)
With the function and trigger created:
create function countdays(t1 date, t2 date)
returns INT
return (timestampdiff(16, char(timestamp(t2) - timestamp(t1))))
create trigger totaldays
after insert on bookingdetail
referencing new as n
for each row mode db2sql
update booking
set bk_totaldays =
countdays((select date_in from bookingdetail), (select date_out from
bookingdetail))
where booking.bk_id = n.bk_id;
I have no problem executing these syntax, but when I try to input a new record inside Booking Detail to let the trigger triggers in Booking, errors occured, may I ask why? Thanks in advance.
Look at the information provided by the SQL error:
db2 ? SQL0811
SQL0811N The result of a scalar fullselect, SELECT INTO statement, or
VALUES INTO statement is more than one row.
So this part of your trigger expressions returns more than 1 row
set bk_totaldays = countdays((select date_in from bookingdetail),
(select date_out from bookingdetail))
Fix this to return a single row.
#MichaleTiefenbacher is correct about the cause of the error, but he's wrong about what to do about it. Let's take another look at your trigger again:
create trigger totaldays
after insert on bookingdetail
referencing new as n -- wait, what's this?
for each row mode db2sql
update booking
set bk_totaldays =
countdays((select date_in from bookingdetail), (select date_out from bookingdetail))
where booking.bk_id = n.bk_id;
You have a reference to the value you just inserted! When using FOR EACH ROW, the table reference NEW refers to the singular row just inserted. So you don't even need to look at the full table, just use what you just worked with:
create trigger totaldays
after insert on bookingdetail
referencing new as n
for each row mode db2sql
update booking
set bk_totaldays = countdays(n.date_in, n.date_out)
where booking.bk_id = n.bk_id;
(As I mentioned in my comment to your question, you'll likely want to change how you calculate the days, but that's irrelevant for this)

Dataintegrity between tables in SQL Server

Is it possible to add data integrity between columns in different tables in SQL Server?
I have table Pay with column Date and table Orders with column DateofOrder. And I would like to add the data integrity so the Date cannot be earlier than the DateofOrder.
And when the user insert there the same date or even earlier database would show error.
I think you mean something like this, here done with a trigger;
CREATE TRIGGER trig_pay ON Pay
FOR INSERT, UPDATE
AS
IF EXISTS(SELECT *
FROM [Order] o
JOIN inserted i
ON o.id = i.payment_id
WHERE DateOfOrder>[date])
BEGIN
RAISERROR ('Sorry, Dave', 16, 1)
ROLLBACK;
RETURN;
END
INSERT INTO [Order] values (1, GETDATE()); -- Order today
INSERT INTO Pay values (1, DATEADD(dd, -1, getdate())); -- Pay yesterday
> Sorry, Dave
Yes, you can do it by using INSTEAD OF INSERT Trigger.
You would have to use an INSTEAD OF or AFTER trigger to enforce this, you can't do it declaratively. Well you could use a check constraint with a TVF or something but I've never tried that.
I'd show sample code but I'm not sure what payroll has to do with orders. If a new order comes in, what pay date must be later than or equal to the order date? Is there some other column that relates these two tables?
It is possible without resorting to triggers.
The idea is to add the DateofOrder in the Orders table to its existing key -- let's call it order_id -- to create a compound superkey, then reference this superkey (rather than the simple key solely order_id) in the Pay table.
Here are the bare bones:
CREATE TABLE Orders
(
order_id CHAR(10) NOT NULL,
DateofOrder DATE NOT NULL,
UNIQUE (order_id), -- simple candidate key
UNIQUE (DateofOrder, order_id) -- compund superkey
);
CREATE TABLE Pay
(
order_id CHAR(10) NOT NULL,
DateofOrder DATE NOT NULL,
FOREIGN KEY (DateofOrder, order_id)
REFERENCES Orders (DateofOrder, order_id),
DateOfPayment DATE NOT NULL,
CHECK (DateofOrder < DateOfPayment),
UNIQUE (order_id)
);

Can a Check constraint relate to another table?

Let's say I have one table called ProjectTimeSpan (which I haven't, just as an example!) containing the columns StartDate and EndDate.
And that I have another table called SubProjectTimeSpan, also containing columns called StartDate and EndDate, where I would like to set a Check constraint that makes it impossible to set StartDate and EndDate to values "outside" the ProjectTimeSpan.StartDate to ProjectTimeSpan.EndDate
Kind of a Check constraint that knows about another tables values...
Is this possible?
In response to your comment on GSerg's answer, here's an example check constraint using a function:
alter table YourTable
add constraint chk_CheckFunction
check (dbo.CheckFunction() = 1)
Where you can define the function like:
create function dbo.CheckFunction()
returns int
as begin
return (select 1)
end
The function is allowed to reference other tables.
You can create a user-defined function that does the check and returns 1 or 0, then create a check constraint on it, providing project id and the dates as the parameters.
Make a compound key of the ProjectTimeSpan table's key combined with the StartDate and EndDate columns, then use this compound key for your foreign key reference in your SubProjectTimeSpan table. This will give you the ability to write the necessary row-level CHECK constraints in the SubProjectTimeSpan table e.g.
CREATE TABLE ProjectTimeSpan
(
project_ID INTEGER NOT NULL UNIQUE, -- key
StartDate DATE NOT NULL,
EndDate DATE NOT NULL,
CHECK (StartDate < EndDate),
UNIQUE (project_ID, StartDate, EndDate) -- compound key
-- other project columns here...
);
CREATE TABLE SubProjectTimeSpan
(
project_ID INTEGER NOT NULL,
StartDate DATE NOT NULL,
EndDate DATE NOT NULL,
FOREIGN KEY (project_ID, StartDate, EndDate)
REFERENCES ProjectTimeSpan (project_ID, StartDate, EndDate)
ON DELETE CASCADE
ON UPDATE CASCADE,
sub_StartDate DATE NOT NULL,
sub_EndDate DATE NOT NULL,
CHECK (sub_StartDate < sub_EndDate),
CHECK (StartDate <= sub_StartDate), -- sub project can't start before main project
CHECK (sub_EndDate <= EndDate) -- sub project can't end after main project
-- other sub project columns here...
);
You certainly can do this as many answers have shown. However, you should be aware that SQL Server seems to have trouble with CHECK CONSTRAINTs that use UDFs:
https://dba.stackexchange.com/questions/12779/how-are-my-sql-server-constraints-being-bypassed
You need to add constraint on the parent and the children table because the subproject can't be out of the project range but the project range can't move out of all the subproject too.
In these kind of situations, you should defer the check of the constraint on an upper level (webservice, application) with a transaction to ensure your data are in a valid state after multiple query on both table !
It is absolutely possible, and actually quite simple. As per your example:
create or alter function dbo.Check_SubProjectTimeSpan_ProjectTimeSpan_Dates(
#ProjectTimeSpanId int
, #StartDate date
, #EndDate date
)
returns bit
as
begin
if exists (select *
from dbo.ProjectTimeSpan as pts
where pts.Id = #ProjectTimeSpanId
and pts.StartDate >= #StartDate
and pts.EndDate <= #EndDate)
begin
return 1
end
return 0
end
go
alter table dbo.SubProjectTimeSpan add constraint
CK_SubProjectTimeSpan_ProjectTimeSpan_ProjectTimeSpanId_StartDate_EndDate
check (
dbo.Check_SubProjectTimeSpan_ProjectTimeSpan_Dates(
ProjectTimeSpanId, StartDate, EndDate) = 1
)
Please be aware however, that this will not be particularly performant.