Using sql function on CHECK constraint for newly inserted row - sql

First of all i need help with this for my bachelor thesis. I'm doing the whole database on sql server 2008 Release 2.
The problem is with check constraint that is using a function that is working on her own but not with the use in the constraint. The result of the constraint should be something like this: An employee could go only on one bussines trip per day.
Table Bussines trips:
CREATE TABLE SluzebniCesta(
idSluzCesty int PRIMARY KEY NOT NULL,
DatumCesty DATE NOT NULL,
CasOdjezdu TIME(0) NOT NULL,
CasPrijezdu TIME(0),
CONSTRAINT Odjezd_prijezd CHECK(CasPrijezdu > DATEADD(hour,2,CasOdjezdu))
);
Table that contains the employs that goes on bussines trip:
CREATE TABLE ZamNaCeste(
idZamNaCeste int PRIMARY KEY NOT NULL,
SluzebCestaID int NOT NULL,
ZamestnanecID int NOT NULL,
FOREIGN KEY (ZamestnanecID) REFERENCES Zamestnanec(idZamestnance),
FOREIGN KEY (SluzebCestaID) REFERENCES SluzebniCesta(idSluzCesty)
);
Foreign key ZamestnanecID is an employee's id and SluzebCestaID is the bussines trip id.
Now the function :
CREATE FUNCTION myCheckZamNaCeste(#SluzebCestaID int, #ZamestnanecID int)
RETURNS int
AS
BEGIN
DECLARE #retVal int;
DECLARE #Zamestnanec int;
DECLARE #SluzebniCesta int;
SET #Zamestnanec = (SELECT idZamestnance FROM Zamestnanec WHERE idZamestnance=#ZamestnanecID);
SET #SluzebniCesta = (SELECT idSluzCesty FROM SluzebniCesta WHERE idSluzCesty=#SluzebCestaID);
IF EXISTS ( SELECT DatumCesty FROM SluzebniCesta
WHERE idSluzCesty = #SluzebniCesta
AND DatumCesty IN (SELECT DatumCesty FROM ZamNaCeste
LEFT JOIN SluzebniCesta
ON ZamNaCeste.SluzebCestaID = SluzebniCesta.idSluzCesty
WHERE ZamestnanecID=#Zamestnanec))
BEGIN
SET #retVal=0;
END
ELSE
BEGIN
SET #retVal=1;
END
return #retVal
END
GO
And the alter table for the table that contains evidence of employee and their bussines trips:
ALTER TABLE ZamNaCeste
ADD CONSTRAINT check_cesty_zamestnance CHECK(dbo.myCheckZamNaCeste(SluzebCestaID,ZamestnanecID)=1);
And when I try to enter any new row the constraint is broken even if the function gives the right data. return 1 is the good result ....

In the first place, I'm not sure but it looks like the two set statements in the function are going out to retrieve from tables exactly the same values they already have from being passed in as parameters.
In the second place, I don't see anything limiting trips in the same day. Anywhere.
If you wanted to limit a trip by an employee to one per day, that is easy.
CREATE TABLE ZamNaCeste(
idZamNaCeste int PRIMARY KEY NOT NULL,
SluzebCestaID int NOT NULL,
ZamestnanecID int NOT NULL,
TripDate date not null,
FOREIGN KEY (ZamestnanecID) REFERENCES Zamestnanec(idZamestnance),
FOREIGN KEY (SluzebCestaID) REFERENCES SluzebniCesta(idSluzCesty),
constraint UQ_OneTripPerDay unique( ZamestnanecID, TripDate )
);
The unique constraint ensures the same employee cannot log more than one trip on the same day.

Well in the end i solved with a more sophisticated and better looking solution. The employ is limited with the times of arrival and departure. And i solved it with a function that returns number of incorrect occurences, if its zero than its all right and it works:
SELECT COUNT(*) FROM(SELECT * FROM SluzebniCesta JOIN ZamNaCeste
ON SluzebniCesta.idSluzCesty = ZamNaCeste.SluzebCestaID) AS a
JOIN (SELECT * FROM SluzebniCesta2 JOIN ZamNaCeste
ON SluzebniCesta.idSluzCesty = ZamNaCeste.SluzebCestaID)AS b
ON a.SluzebCestaID b.SluzebCestaID
AND a.CasOdjezdu b.CasOdjezdu
AND a.ZamestnanecID = b.ZamestnanecID
AND (SELECT SluzebniCesta.DatumCesty FROM SluzebniCesta
WHERE SluzebniCesta.idSluzCesty = a.SluzebCestaID) = (SELECT SluzebniCesta.DatumCesty
FROM SluzebniCesta WHERE SluzebniCesta.idSluzCesty = b.SluzebCestaID)

Related

Trigger to check if the combination of two field exist

I want to create a trigger which will check if my showtime with Cinema Hall Id exist or not. This will prevent me from booking one Cinema hall at two same Showtimes.
Secondly in this trigger I am also checking if the showtime I am assigning to movie lies in movie release and last date range or not.
But I don't know why none of the statement is working.
Create table Movie([Movie_ID] int primary key not null,[Movie_Name] varchar(50) Unique not null,[Realease_Date] date not Null,[Last_Date] date,Runtime time(0) not null ,Status varchar(20) not null,Rating float)
Create table [Showtime]([Showtime_ID] int primary key not null,Date date not null,Time time(0) not Null)
Create table [Cinema Halls]([Cinema_Halls_ID] int primary key not null,[Total_Seats] int not Null)
Create table [Movie Schedule] (
[Movie_Schedule_ID] int primary key not null,
[Movie_ID] int NOT null,
[Showtime_ID] int not null,
Cinema_Halls_ID int not null
Constraint fk_M_ID FOREIGN KEY ([Movie_ID]) REFERENCES Movie([Movie_ID]),
Constraint fk_Sh_ID FOREIGN KEY ([Showtime_ID]) REFERENCES Showtime([Showtime_ID]),
Constraint fk_C_ID FOREIGN KEY ([Cinema_Halls_ID]) REFERENCES [Cinema Halls] ([Cinema_Halls_ID])
)
/*Trigger Stops duplicate booking of Cinema halls and invalid showtime of movie*/
Create Trigger Trigger_Movie_Shedule
On "Movie Schedule"
After Insert,Update
As
declare #Cinema_Halls_ID int ,#Showtime_ID int,#Movie_ID int,#Release_Date Date,#Last_Date Date, #Showtime_Date date;
Select #Cinema_Halls_ID =Cinema_Halls_ID from inserted ;
Select #Showtime_ID=Showtime_ID from inserted;
Select #Movie_ID=Movie_ID from inserted;
Select Showtime_Date=Date from Showtime where Showtime_ID=#Showtime_ID
Select #Release_Date= Release_Date from Movie where Movie_ID=#Movie_ID;
Select #Last_Date=Last_Date from Movie where Movie_ID=#Movie_ID;
IF EXISTS (select count (Showtime_ID) from "Movie Schedule"
where Showtime_ID = #Showtime_ID and Cinema_Halls_ID = #Cinema_Halls_ID )
BEGIN
PRINT'This Cinema Hall is Already Booked'
Rollback Transaction;
return
END
ELSE IF (#Showtime_DATE >= #Release_Date and #Showtime_Date<= #Last_Date)
BEGIN
PRINT'Movie Showtime not in Range'
Rollback Transaction;
return
END
I think this is what you are looking for. The changes/improvements/best practices are:
Uses set-based logic, which you should always aim to do in a relational database.
Uses Inserted as a table rather than a single row
Semi-colon line terminators
set nocount on
Uses throw
Uses [] instead of ""
Fixed "in range" logic and detection of duplicate logic
CREATE TRIGGER Trigger_Movie_Shedule
ON [Movie Schedule]
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS (
SELECT 1
FROM [Movie Schedule] S
-- Restrict the check to the inserted/updated records
INNER JOIN Inserted I on I.Showtime_ID = S.Showtime_ID and I.Cinema_Halls_ID = S.Cinema_Halls_ID
GROUP BY S.Showtime_ID, S.Cinema_Halls_ID
-- If more than one row exists we have a problem Houston
HAVING COUNT(*) > 1
) BEGIN
-- Rolls back, returns and provides an error message all in one.
THROW 51000, 'This Cinema Hall is Already Booked',1;
END; ELSE IF EXISTS (
SELECT 1
FROM Inserted I
INNER JOIN Movie M ON M.Movie_ID = I.Movie_ID
INNER JOIN ShowTime S ON S.ShowTime_ID = I.ShowTime_ID
-- WHERE S.Showtime_DATE >= M.Release_Date and S.Showtime_Date < M.Last_Date
-- Think you logic detects when it *is* in range, whereas the error is when its out of range
WHERE S.Showtime_DATE < M.Release_Date or S.Showtime_Date > M.Last_Date
) BEGIN
-- Rolls back, returns and provides an error message all in one.
THROW 51000, 'Movie Showtime not in Range',1;
END;
END;
Note: I highly recommend reading Using Inserted and Deleted as it explains very clearly how to write triggers.

Having troubles with Identity field of SQL-SERVER

I'm doing a school project about a school theme where I need to create some tables for Students, Classes, Programmes...
I want to add a Group to determined classes with an auto increment in group_id however I wanted the group_id variable to reset if I change any of those attributes(Classes_id,courses_acronym,year_Semesters) how can I reset it every time any of those change??
Here is my table:
CREATE TABLE Classes_Groups(
Classes_id varchar(2),
Group_id INT IDENTITY(1,1),
courses_acronym varchar(4),
year_Semesters varchar(5),
FOREIGN KEY (Classes_id, year_Semesters,courses_acronym) REFERENCES Classes(id,year_Semesters, courses_acronym),
PRIMARY KEY(Classes_id,courses_acronym,year_Semesters,Group_id)
);
Normally, you do not (need to) reset the identity column of a table. An identity column is used to create unique values for every single record in a table.
So you want to generate entries in your groups table based on new entries in your classes table. You might create a trigger on your classes table for that purpose.
Since Group_id is already unique by itself (because of its IDENTITY), you do not need other fields in the primary key at all. Instead, you may create a separate UNIQUE constraint for the combination (Classes_id, courses_acronym, year_Semesters) if you need it.
And if the id field of your classes table is an IDENTITY column too, you could define a primary key in your classes table solely on that id field. And then your foreign key constraint in your new groups table can only include that Classes_id field.)
So much for now. I guess that your database design needs some more additional tuning and tweaking. ;)
where are you setting the values from?, you can have a stored proc and in your query have the columns have an initial value set when stored proc is hit assuming there are values at the beginning
.Then use an IF statement.
declare #initial_Classes_id varchar(2) = --initial value inserted
declare #initial_courses_acronym varchar(4) = --initial value inserted
declare #initial_year_Semesters varchar(5) = --initial value inserted
declare #compare_Classes_id varchar(2) = (select top 1 Classes_id from Classes_Groups order by --PK column desc for last insert); l would add Dateadded and then order with last insert date
declare #compare_courses_acronym varchar(2) = (select top 1 Classes_id from Classes_Groups where Classes_id = #compare_Classes_id);
declare #compare_year_Semesters varchar(2) = (select top 1 Classes_id from Classes_Groups where Classes_id = #compare_Classes_id);
IF (#initial_Classes_id != #compare_Classes_id OR #initial_courses_acronym != #compare_courses_acronym OR #initial_year_Semesters != #compare_year_Semesters)
BEGIN
DBCC CHECKIDENT ('Group_id', RESEED, 1)
Insert into Classes_Groups (courses_acronym,year_Semesters)
values (
courses_acronym,
year_Semesters
)
END
ELSE
BEGIN
Insert into Classes_Groups (courses_acronym,year_Semesters)
values (
courses_acronym,
year_Semesters
)
END
NB: would advice to use int on the primary key. Unless you have a specific purpose of doing so.

Setting a column based on conditions when INSERTING data in ORACLE

Currently working on a basketball performance database. The issue I'm having is storing the winner of a match.
Match table is currently like so:
CREATE TABLE Matches(
M_ID int CONSTRAINT pk_Match PRIMARY KEY,
M_Date Date NOT NULL,
M_Location varchar(20),
M_HomeTeam int NOT NULL,
M_AwayTeam int NOT NULL,
M_HomeScore int NOT NULL,
M_AwayScore int NOT NULL,
M_Winner int,
CONSTRAINT fk_TeamHome foreign key (M_HomeTeam) REFERENCES Team(T_ID),
CONSTRAINT fk_TeamAway foreign key (M_AwayTeam) REFERENCES Team(T_ID)
)
What I want is the value of M_Winner to be set to M_HomeTeam & M_AwayTeam foreign keys based on their scores.
I've been able to do this with this update statement
UPDATE Matches
SET M_Winner = CASE
WHEN M_HomeScore > M_AwayScore
THEN M_HomeTeam
WHEN M_AwayScore > M_HomeScore
THEN M_AwayTeam
END;
However I need it to work when the data is inserted.
Any ideas?
In Oracle 11g+, you can use a virtual computed column:
ALTER TABLE matches
ADD m_winner as (CASE WHEN M_HomeScore > M_AwayScore
THEN M_HomeTeam
WHEN M_AwayScore > M_HomeScore
THEN M_AwayTeam
END)
You can define a trigger for that:
CREATE OR REPLACE TRIGGER trg_ins_match
BEFORE INSERT ON Matches
FOR EACH ROW
BEGIN
IF :new.M_HomeScore > :new.M_AwayScore THEN
:new.M_Winner = :new.M_HomeTeam
ELSE
:new.M_Winner = :new.M_AwayTeam
END IF;
END;
Read about triggers in the Oracle Developer's Guide

How do you enforce unique across 2 tables in SQL Server

Requirements:
Every employee has a unique ID. (EPID)
A employee can only be either one of below,
FT - Full Time
PT - Part Time
Any employee can never be both FT and PT.
FT & PT have lots of different fields to capture.
Implementation:
Create Table EmpFT( EPID int primary key, F1, F2, etc)
Create Table EmpPT( EPID int primary key, P1, P2, etc)
--This does not prevent same EPID on both EmpFT and EmpPT.
How do you implement No. 3 in database?
I am using SQL Server 2012 standard edition.
Try this method:
CREATE TABLE Emp(EPID INT PRIMARY KEY,
t CHAR(2) NOT NULL, UNIQUE (EPID,t));
CREATE TABLE EmpFT(EPID INT PRIMARY KEY, ... other columns
t CHAR(2) NOT NULL CHECK (t = 'FT'),
FOREIGN KEY (EPID,t) REFERENCES Emp (EPID,t));
CREATE TABLE EmpPT(EPID INT PRIMARY KEY, ... other columns
t CHAR(2) NOT NULL CHECK (t = 'PT'),
FOREIGN KEY (EPID,t) REFERENCES Emp (EPID,t));
You can add check constraints. Something like this for both tables
ALTER TABLE EmpFT
ADD CONSTRAINT chk_EmpFT_EPID CHECK (dbo.CHECK_EmpPT(EPID)= 0)
ALTER TABLE EmpPT
ADD CONSTRAINT chk_EmpPT_EPID CHECK (dbo.CHECK_EmpFT(EPID)= 0)
And the functions like so:
CREATE FUNCTION CHECK_EmpFT(#EPID int)
RETURNS int
AS
BEGIN
DECLARE #ret int;
SELECT #ret = count(*) FROM EmpFT WHERE #EPID = EmpFT.EPID
RETURN #ret;
END
GO
CREATE FUNCTION CHECK_EmpPT(#EPID int)
RETURNS int
AS
BEGIN
DECLARE #ret int;
SELECT #ret = count(*) FROM EmpPT WHERE #EPID = EmpPT.EPID
RETURN #ret;
END
GO
Further reading here:
http://www.w3schools.com/sql/sql_check.asp
http://technet.microsoft.com/en-us/library/ms188258%28v=sql.105%29.aspx
You could create a combined table for all employees. The FT and PT tables could use foreign keys to the employee table.
You cannot have primary keys span tables. So, one method is to create a table employee with the appropriate constraints. This might look like this:
create table employee (
EPID int not null identity(1, 1) primary key,
FTID int references empft(empftid),
PTID int references emppt(empptid),
CHECK (FTID is not null and PTID is null or FTID is null and PTID is not null),
UNIQUE (FTID),
UNIQUE (PTID)
. . .
);
create table empft (
EmpFTId int not null identity(1, 1) primary key,
. . .
);
create table emppt (
EmpPTId int not null identity(1, 1) primary key,
. . .
);
Of course, you could also use triggers if you wanted to.

SQL Server 2008 Foreign Keys that are auto indexed

Are Foreign Keys in SQL Server 2008 are automatically indexed with a value? For Example. if I add a value in my Primary key (or auto incremetend) in may parent table will the table that has a foreign key referenced to that key will automatically have the same value? or I Have to do it explicitly?
No, if you create a foreign key in a child table, it will not automatically get populated when a parent row gets inserted. If you think about this it makes sense. Let's say you have a table like:
CREATE TABLE dbo.Students
(
StudentID INT IDENTITY(1,1) PRIMARY KEY,
Name SYSNAME
);
CREATE TABLE dbo.StudentLoans
(
LoanID INT IDENTITY(1,1) PRIMARY KEY,
StudentID INT FOREIGN KEY REFERENCES dbo.Students(StudentID),
Amount BIGINT -- just being funny
);
What you are suggesting is that when you add a row to Students, the system should automatically add a row to StudentLoans - but what if that student doesn't have a loan? If the student does have a loan, what should the amount be? Should the system pick a random number?
Typically what will happen in this scenario is that you'll be adding a student and their loan at the same time. So if you know the loan amount and the student's name, you can say:
DECLARE
#Name SYSNAME = N'user962206',
#LoanAmount BIGINT = 50000,
#StudentID INT;
INSERT dbo.Students(Name)
SELECT #Name;
SELECT #StudentID = SCOPE_IDENTITY();
INSERT dbo.StudentLoans(StudentID, Amount)
SELECT #StudentID, #LoanAmount;