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

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

Related

Creating triggers and selecting the sum of shipped order with two variables

I currently have this assignment where we are supposed to create triggers to block the insert of a shipment depending on whether they exceed the total or not.
I have to sum all the shipped quantities and group them by order and product number.
Here are my tables to give an outlook:
My variables are in French, I'm sorry
CREATE TABLE LigneLivraison
(noLivraison NUMBER(19) NOT NULL,
noProduit NUMBER(19) NOT NULL,
noCommande NUMBER(19) NOT NULL,
quantiteLivree NUMBER(19) NOT NULL,
PRIMARY KEY (noLivraison),
FOREIGN KEY (noLivraison) REFERENCES Livraison,
FOREIGN KEY (noProduit) REFERENCES Produit,
FOREIGN KEY (noCommande) REFERENCES Commande
)
/
CREATE TABLE LigneCommande
(noCommande NUMBER(19) NOT NULL,
noProduit NUMBER(19) NOT NULL,
quantite NUMBER(19) NOT NULL,
CHECK (quantite > 0),
PRIMARY KEY (noCommande, noProduit),
FOREIGN KEY (noCommande) REFERENCES Commande,
FOREIGN KEY (noProduit) REFERENCES Produit
)
/
When I try to execute it, Oracle mentions that the SQL command does not end correctly
Here is what I have so far for the first trigger.
CREATE OR REPLACE TRIGGER bloquerInsertionCommande
BEFORE INSERT
ON LigneLivraison
REFERENCING
NEW AS NouvelleLivraison
FOR EACH ROW
BEGIN
SELECT LigneLivraison.noProduit, LigneLivraison.noCommande, LigneCommande.quantite, SUM(LigneLivraison.quantiteLivree) shipped, (LigneCommande.quantite - shipped) Total
FROM LigneLivraison, LigneCommande
WHERE LigneLivraison.noCommande = LigneCommande.noCommande
GROUP BY noCommande AND noProduit;
IF :NouvelleLivraison.quantiteLivree > Total THEN raise_application_error(-20100, 'La quantite a livrer est trop elevee');
END IF;
END;
/
Since shipping can be made seperately and an x number of times, I need to add all the same product of a certain order number to compare it with what the client originally ordered.
Your create table commands are not working as there are multiple issues as mentioned in the comments inline in following code:
CREATE TABLE LigneLivraison
(noLivraison NUMBER(19) NOT NULL,
noProduit NUMBER(19) NOT NULL,
noCommande NUMBER(19) NOT NULL,
quantiteLivree NUMBER(19) NOT NULL,
PRIMARY KEY (noLivraison),
FOREIGN KEY (noLivraison) REFERENCES Livraison(pk_column_name_of_livraison_table), -- you need to add column name of referencing table here
FOREIGN KEY (noProduit) REFERENCES Produit(pk_column_name_of_produit_table), -- you need to add column name of referencing table here
FOREIGN KEY (noCommande) REFERENCES Commande(pk_column_name_of_commande_table) -- you need to add column name of referencing table here
); -- ending statement with ;
-- / -- this is not needed in sql statements
CREATE TABLE LigneCommande
(noCommande NUMBER(19) NOT NULL,
noProduit NUMBER(19) NOT NULL,
quantite NUMBER(19) NOT NULL CHECK (quantite > 0), -- combined it in single column level constraint
PRIMARY KEY (noCommande, noProduit),
FOREIGN KEY (noCommande) REFERENCES Commande(pk_column_name_of_commande_table), -- you need to add column name of referencing table here
FOREIGN KEY (noProduit) REFERENCES Produit(pk_column_name_of_produit_table) -- you need to add column name of referencing table here
);
--/
Also, pk of referencing table is written in comment for clarity. You can use pk or unique key in references clause according to your requirement.
After your tables are created properly then only you will be able to identify proper error in the trigger.

correcting errors in sql code

I am new to this website, i hope that i ask the question the right way
-- Create a Database table to represent the "FACT" entity.
CREATE TABLE FACT
(
Time_id DATE NOT NULL,
Area_id INTEGER NOT NULL,
Reported_crime_id INTEGER NOT NULL,
Crime_status VARCHAR(8) NOT NULL,
no_of_crime INTEGER NOT NULL,
Max_crime INTEGER NOT NULL,
Avg_crime INTEGER NOT NULL,
Min_crime INTEGER NOT NULL,
date_reported DATE NOT NULL,
-- Specify the PRIMARY KEY constraint for table "FACT".
-- This indicates which attribute(s) uniquely identify each row of data.
CONSTRAINT pk_fact PRIMARY KEY (Time_id, Area_id, Reported_crime_id,
Crime_status)
);
-- Create a Database table to represent the "Crime_Dim" entity.
CREATE TABLE Crime_Dim
(
REPORTED_CRIME_ID INTEGER NOT NULL,
CRIME_TYPE_Desc VARCHAR(50),
DATE_REPORTED DATE NOT NULL,
Crime_type_id INTEGER NOT NULL,
-- Specify the PRIMARY KEY constraint for table "Crime_Dim".
-- This indicates which attribute(s) uniquely identify each row of data.
CONSTRAINT pk_crime_dim PRIMARY KEY (REPORTED_CRIME_ID)
);
-- Create a Database table to represent the "Location_Dim" entity.
CREATE TABLE Location_Dim
(
AREA_ID INTEGER NOT NULL,
AREA_Name VARCHAR(30) NOT NULL,
Area_code INTEGER NOT NULL,
Force_id INTEGER NOT NULL,
-- Specify the PRIMARY KEY constraint for table "Location_Dim".
-- This indicates which attribute(s) uniquely identify each row of data.
CONSTRAINT pk_location_dim PRIMARY KEY (AREA_ID)
);
-- Create a Database table to represent the "Time_Dim" entity.
CREATE TABLE Time_Dim
(
Time_id INTEGER NOT NULL,
day_id INTEGER NOT NULL,
Month_id INTEGER NOT NULL,
Year INTEGER,
-- Specify the PRIMARY KEY constraint for table "Time_Dim".
-- This indicates which attribute(s) uniquely identify each row of data.
CONSTRAINT pk_time_dim PRIMARY KEY (Time_id)
);
-- Create a Database table to represent the "Reported_crime_dim" entity.
CREATE TABLE Reported_crime_dim
(
Crime_status VARCHAR(20) NOT NULL,
Date_reported DATE NOT NULL,
-- Specify the PRIMARY KEY constraint for table "Reported_crime_dim".
-- This indicates which attribute(s) uniquely identify each row of data.
CONSTRAINT pk_reported_crime_dim PRIMARY KEY (Crime_status)
);
-- i.e. tables may be referenced before they have been created. This method is therefore safer.
-- Alter table to add new constraints required to implement the "FACT_Time_Dim" relationship
-- This constraint ensures that the foreign key of table "FACT"
-- correctly references the primary key of table "Time_Dim"
ALTER TABLE FACT
ADD CONSTRAINT fk1_fact_to_time_dim FOREIGN KEY(Time_id) REFERENCES Time_Dim(
Time_id) ON DELETE RESTRICT ON UPDATE RESTRICT;
-- Alter table to add new constraints required to implement the "FACT_Location_Dim" relationship
-- This constraint ensures that the foreign key of table "FACT"
-- correctly references the primary key of table "Location_Dim"
ALTER TABLE FACT
ADD CONSTRAINT fk2_fact_to_location_dim FOREIGN KEY(AREA_ID) REFERENCES
Location_Dim(AREA_ID) ON DELETE RESTRICT ON UPDATE RESTRICT;
-- Alter table to add new constraints required to implement the "FACT_Crime_Dim" relationship
-- This constraint ensures that the foreign key of table "FACT"
-- correctly references the primary key of table "Crime_Dim"
ALTER TABLE FACT
ADD CONSTRAINT fk3_fact_to_crime_dim FOREIGN KEY(REPORTED_CRIME_ID) REFERENCES
Crime_Dim(REPORTED_CRIME_ID) ON DELETE RESTRICT ON UPDATE RESTRICT;
-- Alter table to add new constraints required to implement the "FACT_Reported_crime_dim" relationship
-- This constraint ensures that the foreign key of table "FACT"
-- correctly references the primary key of table "Reported_crime_dim"
ALTER TABLE FACT
ADD CONSTRAINT fk4_fact_to_reported_crime_dim FOREIGN KEY(Crime_status)
REFERENCES Reported_crime_dim(Crime_status) ON DELETE RESTRICT ON UPDATE
RESTRICT;
--------------------------------------------------------------
-- End of DDL file auto-generation
--------------------------------------------------------------
​
Could someone help me in correcting the errors in the sql code above? when i run the code as mentioned at the last few codes i need to create constraint to prevent Foreign keys in the Fact table and relate them back to their original table if i have got this right...
could someone correct the code and let me know plz
Following this answer, you have to remove ON DELETE RESTRICT and ON UPDATE RESTRICT.
There's no such option in Oracle.

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

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';