don`t understand how trigger works - sql

Trigger should react to deleting row from table Suppliers if there is some supplies in the table Supplies from this supplier, trigger should cancel deleting.
This code allows to delete both, suppliers with supplies and suppliers without supply:
CREATE TRIGGER SuppliersDeleteCondition
ON Suppliers
FOR DELETE
AS
IF EXISTS (
SELECT 1
FROM Supplies Ses
JOIN DELETED D
ON D.SupplierID=Ses.SupplierID
)
BEGIN
RAISERROR ('This supplier has some supplies', 16, 1)
ROLLBACK TRANSACTION
END;
Approx tables view are:
Suppliers(SupplierID,Name,Address,BankDetails), Supplies(SupplyID,SupplierID,ProductID,ImplementationPeriod,Weight,Price)
Here is CREATE TABLE:
CREATE TABLE Suppliers
(
SupplierID INT IDENTITY,
Name VARCHAR(150) NOT NULL UNIQUE,
Address VARCHAR(900) NOT NULL,
BankDetails VARCHAR(9) NOT NULL UNIQUE,
CONSTRAINT pk_SupplierID PRIMARY KEY (SupplierID),
CONSTRAINT chk_Name_Suppliers CHECK (NOT Name LIKE '%[^a-z ]%' AND NOT Name LIKE '[ ]%' AND NOT Name LIKE '%[ ]' AND NOT Name LIKE '%[ ][ ]%'),
CONSTRAINT chk_Address_Suppliers CHECK (NOT Address LIKE '%[^a-zA-z0-9,./ ]%' AND NOT Address LIKE '[ ]%'),
CONSTRAINT chk_BankDetails_Suppliers CHECK (BankDetails LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]')
);
CREATE TABLE Supplies
(
SupplyID INT IDENTITY,
SupplierID INT,
ProductID INT,
ImplementationPeriod DATE NOT NULL,
Weight REAL NOT NULL,
Price MONEY NOT NULL,
CONSTRAINT pk_SupplyID PRIMARY KEY (SupplyID),
CONSTRAINT fk_SupplierID FOREIGN KEY (SupplierID) REFERENCES Suppliers(SupplierID) ON DELETE CASCADE,
CONSTRAINT fk_ProductID FOREIGN KEY (ProductID) REFERENCES Products(ProductID) ON DELETE CASCADE,
CONSTRAINT chk_Weight_Supplies CHECK (Weight > 0)
);
When I delete the row like this:
DELETE FROM Suppliers WHERE SupplierID=18
this row deleted and I got no error

You have a foreign key with the DELETE CASCADE option so the delete trigger is superfluous. The AFTER DELETE trigger will fire after rows from suppliers and supplies have been deleted so no rows will ever qualify for the EXISTS predicate. No need for the trigger here unless you could remove the foreign key, which I wouldn't recommend. Let SQL Server do the work for you.

Related

Invalid Column Name when Create trigger using SQL server

I encountered several errors when I tried to create a simple trigger using SQL server. Tables are created using:
CREATE TABLE person
(
person_id INT NOT NULL PRIMARY KEY,
name VARCHAR NOT NULL,
phone INT,
birth_date DATE,
address VARCHAR
);
CREATE TABLE volunteers
(
person_id INT NOT NULL PRIMARY KEY,
skill VARCHAR,
FOREIGN KEY (person_id) REFERENCES person(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE physicians
(
person_id INT NOT NULL PRIMARY KEY,
speciality VARCHAR,
phone_number INT,
FOREIGN KEY (person_id) REFERENCES person(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE patients
(
person_id INT NOT NULL PRIMARY KEY,
contact_date DATE,
physician_id INT, -- TODO determine whether NULL able
-- TODO determine ON DELETE ACTION
CHECK (person_id <> physician_id),
FOREIGN KEY (person_id) REFERENCES person(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (physician_id) REFERENCES physicians(person_id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
);
CREATE TABLE outpatients
(
person_id INT NOT NULL PRIMARY KEY,
FOREIGN KEY (person_id) REFERENCES patients(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE visits
(
person_id INT NOT NULL,
date DATE NOT NULL,
comments VARCHAR,
PRIMARY KEY (person_id, date),
FOREIGN KEY (person_id) REFERENCES outpatients(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE employees
(
person_id INT NOT NULL PRIMARY KEY,
date_hire DATE,
FOREIGN KEY (person_id) REFERENCES person(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE nurses
(
person_id INT NOT NULL PRIMARY KEY,
certificate VARCHAR,
care_center_name VARCHAR,
FOREIGN KEY (person_id) REFERENCES employees(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE,
-- FOREIGN KEY (care_center_name) REFERENCES care_centers(name)
-- ON DELETE CASCADE
-- ON UPDATE CASCADE
);
CREATE TABLE care_centers
(
name VARCHAR PRIMARY KEY NOT NULL,
nurses_in_charge INT NOT NULL,
location VARCHAR,
type VARCHAR
-- FOREIGN KEY (nurses_in_charge) REFERENCES registered_nurse(person_id)
);
CREATE TABLE registered_nurse
(
person_id INT NOT NULL PRIMARY KEY,
care_center_name VARCHAR,
FOREIGN KEY (person_id) REFERENCES nurses(person_id),
-- ON DELETE CASCADE
-- ON UPDATE CASCADE,
FOREIGN KEY (care_center_name) REFERENCES care_centers(name)
-- ON DELETE CASCADE
-- ON UPDATE CASCADE
);
ALTER TABLE nurses
ADD constraint nurses__care_centers_FK
FOREIGN KEY (care_center_name) REFERENCES care_centers(name)
ON DELETE CASCADE
ON UPDATE CASCADE;
ALTER TABLE care_centers
ADD constraint care_centers__registered_nurse_FK
FOREIGN KEY (nurses_in_charge) REFERENCES registered_nurse(person_id);
CREATE TABLE beds
(
bed_number INT NOT NULL,
room_number INT NOT NULL,
care_center_name VARCHAR,
PRIMARY KEY (bed_number,room_number),
FOREIGN KEY (care_center_name) REFERENCES care_centers(name)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE residents
(
person_id INT NOT NULL PRIMARY KEY,
date_admitted DATE,
bed_number INT,
room_number INT,
FOREIGN KEY (person_id) REFERENCES patients(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (bed_number, room_number) REFERENCES beds(bed_number, room_number)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE staffs
(
person_id INT NOT NULL PRIMARY KEY,
job_class DATE,
FOREIGN KEY (person_id) REFERENCES employees(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE technicians
(
person_id INT NOT NULL PRIMARY KEY,
skill VARCHAR,
FOREIGN KEY (person_id) REFERENCES employees(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE laboratories
(
name VARCHAR NOT NULL PRIMARY KEY,
location VARCHAR
);
CREATE TABLE assignTechnicianToLab
(
person_id INT NOT NULL,
laboratories_name VARCHAR NOT NULL,
PRIMARY KEY(person_id, laboratories_name),
FOREIGN KEY (person_id) REFERENCES employees(person_id)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (laboratories_name) REFERENCES laboratories(name)
ON DELETE CASCADE
ON UPDATE CASCADE
)
And the trigger I am trying to create:
-- Trigger
/* if nurse has certificate "RN", add to registered_nurse */
CREATE TRIGGER registered_nurse_trigger
ON nurses
FOR INSERT, UPDATE
AS
BEGIN
IF EXISTS (SELECT * FROM inserted where certificate = "RN")
BEGIN
INSERT INTO registered_nurse
VALUES(inserted.person_id, nurses.care_center_name);
END
END;
And I keep getting errors saying that:
Msg 207, Level 16, State 1, Procedure registered_nurse_trigger, Line 6
Invalid column name 'RN'.
Msg 4104, Level 16, State 1, Procedure registered_nurse_trigger, Line 8
The multi-part identifier "inserted.person_id" could not be bound.
Msg 4104, Level 16, State 1, Procedure registered_nurse_trigger, Line 8
The multi-part identifier "nurses.care_center_name" could not be bound.
I've been hanging over here for several hours and I really appreciate anyone can help me with it. Thank you very much!
You can't use VALUES() like that. Use INSERT..SELECT instead:
-- Trigger
/* if nurse has certificate "RN", add to registered_nurse */
CREATE TRIGGER registered_nurse_trigger ON nurses
For INSERT, UPDATE
AS BEGIN
INSERT INTO registered_nurse (person_id, care_center_name)
SELECT person_id, care_center_name
FROM inserted
WHERE certificate = 'RN'
END;
I don't think you need to reference nurses or have that EXISTS() either, but maybe I'm overlooking something.
You are using double quote instead of single quotes in your Create Trigger statement try this and see if it works:
-- Trigger
/* if nurse has certificate "RN", add to registered_nurse */
CREATE TRIGGER registered_nurse_trigger ON nurses
FOR INSERT, UPDATE
AS
BEGIN
IF EXISTS (SELECT * FROM inserted where certificate = 'RN')
BEGIN
INSERT INTO registered_nurse
SELECT person_id, care_center_name FROM Inserted;
END
END;

Check if data exists in another table on insert?

Table A
(
Table_A_ID int
)
Table B
(
Table_B_ID int
Value int
)
Say I want to insert data into Table B, where 'Value' would be the same as a Table_A_ID.
How would I make a constraint or check that the data actually exists in the table on insertion?
You probably need to enforce data integrity not only on INSERT into Table B, but also on UPDATE and DELETE in both tables.
Anyway options are:
FOREIGN KEY CONSTRAINT on Table B
TRIGGERs on both tables
As a last resort if for some reason 1 and 2 is not an option STORED PROCEDUREs for all insert, delete update operations for both tables
The preferred way to go in most cases is FOREIGN KEY CONSTRAINT.
Yap, I agree with #peterm.
Cause, if your both Table_A_ID and Table_B_Id are primary keys for both tables, then you don't even need two tables to store the value. Since, your two tables are seems to be on 'one-to-one' relationship. It's one of the database integrity issues.
I think you didn't do proper normalisation for this database.
Just suggesting a good idea!
I found this example which demonstrates how to setup a foreign key constraint.
Create employee table
CREATE TABLE employee (
id smallint(5) unsigned NOT NULL,
firstname varchar(30),
lastname varchar(30),
birthdate date,
PRIMARY KEY (id),
KEY idx_lastname (lastname)
) ENGINE=InnoDB;
Create borrowed table
CREATE TABLE borrowed (
ref int(10) unsigned NOT NULL auto_increment,
employeeid smallint(5) unsigned NOT NULL,
book varchar(50),
PRIMARY KEY (ref)
) ENGINE=InnoDB;
Add a constraint to borrowed table
ALTER TABLE borrowed
ADD CONSTRAINT FK_borrowed
FOREIGN KEY (employeeid) REFERENCES employee(id)
ON UPDATE CASCADE
ON DELETE CASCADE;
NOTE: This tells MySQL that we want to alter the borrowed table by adding a constraint called ‘FK_borrowed’. The employeeid column will reference the id column in the employee table – in other words, an employee must exist before they can borrow a book.
The final two lines are perhaps the most interesting. They state that if an employee ID is updated or an employee is deleted, the changes should be applied to the borrowed table.
NOTE: See the above URL for more details, this is just an excerpt from that article!
Create a foreign key constraint on the column 'Value' on table B that references the 'Table_A_ID' column.
Doing this will only allow values that exist in table A to be added into the 'Value' field of table B.
To accomplish this you first need to make Table_A_ID column the primary key for table A, or it at least has to have some sort of unique constraint applied to it to be a foreign key candidate.
BEGIN TRANSACTION -- REMOVE TRANSACTION AND ROLLBACK AFTER DONE TESTING
--PUT A PRIMARY KEY ON TABLE A
CREATE TABLE A
( Table_A_ID int CONSTRAINT PK_A_Table_A_ID PRIMARY KEY)
--ON VALUE ADD A FOREIGN KEY CONSTRAINT THAT REFERENCEs TABLE A
CREATE TABLE B
( Table_B_ID int,
[Value] int CONSTRAINT FK_B_Value_A REFERENCES A(Table_A_ID)
)
-- TEST VALID INSERT
INSERT A (Table_A_ID) VALUES (1)
INSERT B (Table_B_ID, [Value]) VALUES (1,1)
--NOT ALLOW TO INSERT A VALUE THAT DOES NOT EXIST IN A
--THIS WILL THROW A FOREIGN KEY CONSTRAINT ERROR
INSERT B (Table_B_ID, [Value]) VALUES (1,2) -- 2 DNE in table A
ROLLBACK
Note: there is no magic to 'FK_B_Value_A' or 'PK_A_Table_A_ID' it simply a naming convention and be called anything. The syntax on the foreign key and primary key lines work like this:
column-definition CONSTRAINT give-the-constraint-a-name REFERENCES table-name ( table-column )
column-definition CONSTRAINT give-the-constraint-a-name PRIMARY KEY

Sybase constraint with foreign key and conditional check against foreign table column value

Is it possible on Sybase to define a constraint(s) which require a column to be a foreign key and also satisfy a condition based on the value of a foreign column, e.g. in the below example tables could a constraint be created on the "product" table such that "product.code is a foreign key of a brand.code which has valid=1"?
CREATE TABLE brand (
code char(8) NOT NULL,
valid int NOT NULL,
rowid numeric(10,0) IDENTITY,
CONSTRAINT brand_pk PRIMARY KEY (code),
CONSTRAINT valid_check CHECK (valid IN (0,1))
)
CREATE TABLE product (
code char(8) NOT NULL,
CONSTRAINT product_pk PRIMARY KEY (code)
)
I think it's best to change the structure just a little bit.
CREATE TABLE brand (
code char(8) NOT NULL,
valid int NOT NULL,
rowid numeric(10,0) IDENTITY,
CONSTRAINT brand_pk PRIMARY KEY (code),
-- The following UNIQUE constraint lets the pair of values be the target of
-- a foreign key reference.
CONSTRAINT brand_is_valid UNIQUE (code, valid),
CONSTRAINT valid_check CHECK (valid IN (0,1))
);
CREATE TABLE product (
code char(8) NOT NULL,
valid int NOT NULL,
-- The column "code" is a PK in the referenced table, so this still works. It's
-- a 1:0 or 1:1 relationship.
CONSTRAINT product_pk PRIMARY KEY (code),
-- The next constraint requires a unique constraint on the pair of
-- columns in the table "brand". By itself, it references every row
-- in "brand". That's too many rows.
CONSTRAINT product_fk FOREIGN KEY (code, valid)
REFERENCES brand (code, valid),
-- But this constraint restricts the foreign key references to only those
-- rows that have valid = 1 in the table "brand".
CHECK (valid = 1)
);
In order to bypass the creation of a foreign-key based on the "valid" condition, you will need to modify your table design and create a trigger to set the product.code = NULL. Pardon my syntax (I haven't coded Sybase for a while), but this is the general idea:
Add a new column to serve as the primary key since we will need to set product.code = NULL when valid=0:
CREATE TABLE product (
rowid int identity primary key,
code char(8) NULL,
CONSTRAINT brand_fk FOREIGN KEY (code) REFERENCES brand(code)
)
Then create a trigger similar to this one:
create trigger FK_WhenValid
on product
for insert
AS
IF (SELECT COUNT(*) FROM brand b inner join inserted i on b.code = i.code AND b.valid=0 ) > 0
BEGIN
UPDATE product SET code = NULL WHERE code in (SELECT i.code from brand b join inserted i on b.code = i.code and b.valid = 0)
END
Note: this trigger only supports product insertions. If "valid" can change, another approach is required.
You could also implement the foreign key as a trigger instead of a declarative constraint and only set product.code = inserted.code when valid = 1

SQL: Can I write a CHECK constraint that validates data in a foreign table using a foreign key?

I am designing a test database in SQL Server 2008 R2 and/or SQL Azure. (All of my code will run on both, so far.)
I have a table with a foreign key, and I need to add a constraint that references a field in the foreign table.
Normally I would have the foreign table manage it's own validation checks, but there are cases where that is impossible (or illogical). I've provided some sample-code that displays what I am trying to accomplish.
CREATE TABLE CustomerOrder
(
ID INT NOT NULL IDENTITY PRIMARY KEY,
CustomerID INT NOT NULL UNIQUE
FOREIGN KEY REFERENCES Customer(ID)
ON DELETE NO ACTION ON UPDATE CASCADE,
ProductID INT NOT NULL UNIQUE
FOREIGN KEY REFERENCES Product(ID)
ON DELETE NO ACTION ON UPDATE CASCADE,
Quantity INT NOT NULL DEFAULT 1,
IsPaid BIT NOT NULL DEFAULT 0
)
GO
CREATE TABLE RMA
(
ID INT NOT NULL IDENTITY PRIMARY KEY,
CustomerOrderID INT NOT NULL UNIQUE
FOREIGN KEY REFERENCES CustomerOrder(ID)
ON DELETE NO ACTION ON UPDATE CASCADE,
-- Add constraint to prevent RMAs from being
-- created for orders that have not been paid.
-- This could be a column constraint, or a table constraint.
CHECK ( CustomerOrderID.IsPaid = 1 )
-- ERROR: 'The multi-part identifier "CustomerOrderID.IsPaid"
-- could not be bound.'
)
GO
In this example, it doesn't make sense to put the CHECK constraint in the CustomerOrder table, because a row in the CustomerOrder table is perfectly happy being unpaid as long as there are no RMAs for the order. Furthermore, a constraint in the CustomerOrder table would still need to reference the RMA table to confirm whether there is an RMA, so the same issue remains.
I've also tried:
CHECK (EXISTS(SELECT co.ID FROM CustomerOrder co
WHERE co.ID=CustomerOrderID AND
co.IsPaid=1))
-- ERROR: 'Subqueries are not allowed in this context.
-- Only scalar expressions are allowed.'
Since this is a static constraint for basic data validation, and will never be referenced by any other object, I'd like to avoid making this into a scalar function or stored procedure.
However, in order to avoid using a scalar function, I will need to define the constraint within SQL (preferrably at the same time my database is deployed and the table is created).
What SQL syntax could I use here to define this type of constraint?
Another option (since you asked for a solution without adding triggers or enlarging the foreign key), is removing the IsPaid column and adding another table for paid orders:
CREATE TABLE CustomerOrder
(
ID INT NOT NULL IDENTITY PRIMARY KEY,
CustomerID INT NOT NULL UNIQUE
FOREIGN KEY REFERENCES Customer(ID)
ON DELETE NO ACTION ON UPDATE CASCADE,
ProductID INT NOT NULL UNIQUE
FOREIGN KEY REFERENCES Product(ID)
ON DELETE NO ACTION ON UPDATE CASCADE,
Quantity INT NOT NULL DEFAULT 1
)
GO
CREATE TABLE CustomerOrderPaid
(
ID INT NOT NULL PRIMARY KEY
FOREIGN KEY REFERENCES CustomerOrder(ID)
ON DELETE NO ACTION ON UPDATE CASCADE
)
GO
CREATE TABLE RMA
(
ID INT NOT NULL IDENTITY PRIMARY KEY,
CustomerOrderID INT NOT NULL UNIQUE
FOREIGN KEY REFERENCES CustomerOrderPaid(ID)
ON DELETE NO ACTION ON UPDATE CASCADE,
)
GO

How can I get around this foreign key constraint having to have a unique name?

I'm not sure why these have to be unique, but from reading the MySQL forums it appears that they do. However, I think it has something more to do with the INDEX name. I have two tables that have foreign key constraints referencing the same primary key on a third table. If it helps, I'm using MySQL workbench to design the schema.
I usually name my foreign key on each table the same name as the primary key it references. I guess this isn't possible. It will create the first table with the foreign key constraint, but when it tries to create the second table it throws an error. Here is the second table it throws the error on:
CREATE TABLE IF NOT EXISTS `joe`.`products_to_categories` (
`product_to_category_id` INT NOT NULL AUTO_INCREMENT ,
`category_id` INT NOT NULL ,
`product_id` INT NOT NULL ,
PRIMARY KEY (`product_to_category_id`) ,
INDEX `category_id` (`category_id` ASC) ,
INDEX `product_id` (`product_id` ASC) ,
CONSTRAINT `category_id`
FOREIGN KEY (`category_id` )
REFERENCES `joe`.`categories` (`category_id` )
ON DELETE CASCADE
ON UPDATE NO ACTION,
CONSTRAINT `product_id`
FOREIGN KEY (`product_id` )
REFERENCES `joe`.`products` (`product_id` )
ON DELETE CASCADE
ON UPDATE NO ACTION)
ENGINE = InnoDB;
I want the foreign key names to be the same as the primary key in both of the other tables. What should I remove here so that I can use these names. What is the best practice here.
It is not possible because you would have a conflict in the filename for the file that is used for the index IIRC. I probably would name the key < tablename >_< column_name > or something like that.
You are creating an index (constraint) by the name of product_id via:
INDEX product_id
Then you are going and creating another constraint (for the foreign key) with the same name:
CONSTRAINT product_id
What you need to do is allow the server to provide a default, unique constraint name by removing the
CONSTRAINT product_id
See this URL: http://dev.mysql.com/doc/refman/5.1/en/innodb-foreign-key-constraints.html
"If the CONSTRAINT symbol clause is given, the symbol value must be unique in the database. If the clause is not given, InnoDB creates the name automatically."
In PostgreSQL, the default for naming indexes is to append "_pkey" and "_fkey" to the name of the primary and foreign key, respectively. So your case would look like:
INDEX `product_id_fkey` (`product_id` ASC) ,
UPDATE: I just tried this and it worked. See if that's what you had in mind.
use test;
create table if not exists test.product
(
product_id int not null auto_increment,
name varchar(80) not null,
primary key(product_id)
);
create table if not exists test.category
(
category_id int not null auto_increment,
name varchar(80) not null,
primary key(category_id)
);
create table if not exists test.product_category
(
product_id int,
category_id int,
primary key(product_id, category_id),
constraint product_id_fkey
foreign key(product_id) references product(product_id)
on delete cascade
on update no action,
constraint category_id_fkey
foreign key(category_id) references category(category_id)
on delete cascade
on update no action
);
insert into test.product(name) values('teddy bear');
insert into test.category(name) values('toy');
insert into test.product_category
select p.product_id, c.category_id from product as p, category as c
where p.name = 'teddy bear' and c.name = 'toy';