I have a table in which I need to ensure that startDate is not later than endDate.
Either date can be updated so the rules have to apply regardless if startDate or endDate (or both) are entered/modified.
Is it better to use triggers, stored procedures or something else? Any sample syntax is appreciated.
Use a check constraint. Much simpler than a trigger.
CREATE TABLE dbo.foo
(
StartDate DATE NOT NULL,
EndDate DATE NOT NULL,
CONSTRAINT CheckEndLaterThanStart CHECK (EndDate >= StartDate)
);
If the table already exists:
ALTER TABLE dbo.foo
ADD CONSTRAINT CheckEndLaterThanStart
CHECK (EndDate >= StartDate);
If you try to insert a start date later than the end date, you'll get:
Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the CHECK constraint "CheckEndLaterThanStart". The conflict occurred in database "AdventureWorks2012", table "dbo.foo".
The statement has been terminated.
Related
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.
I need to add a constraint to one table in my database. The table name is Experience. And there is a column named ToDate. Every time the select statement executes like following.
select ToDate from Experience
It should return current date.
So every time select statement executes, the ToDate column get updated with current date.
I know I can do this with some type of sql trigger but is there a way to do it by sql constraint.
like
alter table add constraint...
Any help will be appreciated.
Thanks
You can use a computed column. That's specified like colname as <expression>:
create table t1(id int, dt as getdate());
insert t1 values (1);
select * from t1;
To add contraint ...
create table tbl (id int identity, dt datetime, colval varchar(10))
ALTER TABLE dbo.tbl
ADD CONSTRAINT col_dt_def
DEFAULT GETDATE() FOR dt;
Example of inserting to the table ..
insert into dbo.tbl(colval)
select 'somevalue'
select * from dbo.tbl
The result will be ..
id dt colval
1 2014-08-19 13:31:57.577 somevalue
You cannot use a constraint, because a constraint is basically a rule on what can go in the table, how the table can relate to others, etc. It has no bearing on the data in the table once it goes into the table. Now if I am understanding you correctly, you want to update the ToDate column whenever you select that column. Now you can't use a trigger either as mentioned here and here. They suggest a stored procedure where you would use an update followed by an insert. This is probably my preferred SQL method to go with if you have to use it repeated, which you seem to have to do. Though Andomar's answer is probably better.
Try this link code make help full
http://www.sqlatoms.com/queries/how-to-use-the-getdate-function-in-sql-server-3/
CREATE TABLE ProductOrders
(
OrderId int NOT NULL PRIMARY KEY IDENTITY,
ProductName nvarchar(50) NOT NULL,
OrderDate datetime NOT NULL DEFAULT GETDATE()
)
Im using SQL/PL developer and I have a table named Appeal, that has 2 attributs OpenDate and CloseDate. And I want to add a constraint to ensure that open date will be smaller than close date. I have a lot of records in this table.
this is my code:
alter table appeal
add constraint Check_Dates
check (OpenDate < CloseDate)
and I get en error saying:
ORA-02293: cannot validate (STSTEM.CHECK_DATES) - check constraint violated
any ieads?
Thanx
Your constraint looks right, I have tested it:
create table appeal ( OpenDate date, CloseDate date);
alter table appeal
add constraint Check_Dates
check (OpenDate < CloseDate);
insert into appeal values ( sysdate, sysdate - 1 );
And here the result:
Schema Creation Failed: ORA-02290: check constraint
(USER_4_44096.CHECK_DATES) violated
Problem is than you have already rows with OpenDate < CloseDate values in your database. Fix it before create constraint. Look behavior changing sentences order:
create table appeal ( OpenDate date, CloseDate date);
insert into appeal values ( sysdate, sysdate - 1 );
alter table appeal
add constraint Check_Dates
check (OpenDate < CloseDate);
And here your issue:
Schema Creation Failed: ORA-02293: cannot validate
(USER_4_E4450.CHECK_DATES) - check constraint violated
Try this
alter table appeal
add constraint Check_Dates
check (OpenDate < CloseDate) ENABLE NOVALIDATE;
You will have check the previous data for errors but any new Data will fall under the CHECK
I have a table in Sql Server where i have Columns : UserId, RoleId, FromDate and Todate.
I want to write a contraint that check same RoleId and UserId are not present for the same date.
Thanks In Advance....
If you don't have time portion or the time portion is the same in all records you can use UNIQUE constraint:
ALTER TABLE yourSchema.yourTable
ADD CONSTRAINT uniqueConstraint1 UNIQUE (RoleId, UserId, FromDate);
This way combination of date, RoleId and UserId can occur only once in table, other attempts to insert the same combination will fail.
Note that this will work if your date field has values for time portion that are the same in every record (for instance 0) or the data type of the field is DATE (which eliminates the time portion).
If your date field has time portion that varies among records, try one of this approaches:
A) Add computed column of definition
ALTER TABLE yourSchema.yourTable
ADD constraintCheckDate AS CAST(FromDate AS DATE)
and add UNIQUE constraint of definition
ALTER TABLE yourSchema.yourTable
ADD CONSTRAINT uniqueConstraint1 UNIQUE (RoleId, UserId, constraintCheckDate)
B) Use trigger to validate data before inserting it, data will be entered only if it doesn't already exist:
CREATE TRIGGER trig1 ON yourSchema.yourTable
INSTEAD OF INSERT
AS
BEGIN
IF NOT EXISTS
(
SELECT *
FROM yourSchema.yourTable t
JOIN inserted i ON
CAST(t.FromDate AS DATE) = CAST(i.FromDate AS DATE)
AND t.RoleId = i.RoleId
AND t.UserId = i.UserId
)
INSERT yourTable(RoleId, UserId, FromDate, ToDate)
SELECT RoleId, UserId, FromDate, ToDate
FROM inserted
ELSE
RAISERROR('Error', 16, 0)
END
GO
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.