PostgreSQL CHECK Constraint on columns other than foreign keys - sql

I have a situation where I want to create a table that associates records from other tables by the id. A constraint of the association is that the year must be the same in the record being associated in each table... Is there a way to get PostgreSQL to CHECK this condition on INSERT?
Table 1:
CREATE TABLE "tenant"."report" (
"id" UUID NOT NULL DEFAULT "pascal".uuid_generate_v1(),
CONSTRAINT "report_pkc_id" PRIMARY KEY ("id"),
"reporting_period" integer NOT NULL,
"name" VARCHAR(64) NOT NULL,
CONSTRAINT "report_uc__name" UNIQUE ("reporting_period", "name"),
"description" VARCHAR(2048) NOT NULL
);
Table 2:
CREATE TABLE "tenant"."upload_file" (
"id" UUID NOT NULL DEFAULT "pascal".uuid_generate_v1(),
CONSTRAINT "upload_file_pkc_id" PRIMARY KEY ("id"),
"file_name" VARCHAR(256) NOT NULL,
"reporting_period" integer
)
Association Table:
CREATE TABLE "tenant"."report_upload_files"
(
"report_id" UUID NOT NULL,
CONSTRAINT "report_upload_files_pkc_tenant_id" PRIMARY KEY ("report_id"),
CONSTRAINT "report_upload_files_fkc_tenant_id" FOREIGN KEY ("report_id")
REFERENCES "tenant"."report" ("id") MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE,
"upload_file_id" UUID NOT NULL,
CONSTRAINT "report_upload_files_fkc_layout_id" FOREIGN KEY ("upload_file_id")
REFERENCES "tenant"."upload_file" ("id") MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE
)
I want to add something like to the association table CREATE statement:
CHECK ("tenant"."report"."reporting_period" = "tenant"."upload_file"."reporting_period")

You're solving problems that you've created yourself.
Your data model is a typical one-to-many relationship. You don't need an association table. Also, you don't need the same column in two related tables, one of them is redundant. Use the model as shown below to avoid typical problems resulting from lack of normalization.
create table tenant.report (
id uuid primary key default pascal.uuid_generate_v1(),
reporting_period integer not null,
name varchar(64) not null,
description varchar(2048) not null,
unique (reporting_period, name)
);
create table tenant.upload_file (
id uuid primary key default pascal.uuid_generate_v1(),
report_id uuid references tenant.report(id),
file_name varchar(256) not null
);
Using this approach there's no need to ensure that the reporting periods match between the associated records.
BTW, I would use text instead of varchar(n) and integer (serial) instead of uuid.

Using a TRIGGER function I was able to achieve the desired effect:
CREATE FUNCTION "tenant".report_upload_files_create() RETURNS TRIGGER AS
$report_upload_files_create$
BEGIN
IF NOT EXISTS (
SELECT
*
FROM
"tenant"."report",
"tenant"."upload_file"
WHERE
"tenant"."report"."id" = NEW."report_id"
AND
"tenant"."upload_file"."id" = NEW."upload_file_id"
AND
"tenant"."report"."reporting_period" = "tenant"."upload_file"."reporting_period"
)
THEN
RAISE EXCEPTION 'Report and Upload File reporting periods do not match';
END IF;
RETURN NEW;
END
$report_upload_files_create$ LANGUAGE plpgsql;
CREATE TRIGGER "report_upload_files_create" BEFORE INSERT ON "tenant"."report_upload_files"
FOR EACH ROW EXECUTE PROCEDURE "tenant".report_upload_files_create();

Related

Upgrading H2 database gives constraint not found exception

I am trying to upgrade my H2 dependency which I use on my testcases from 1.4.200 to 2.1.212 but it gives a constraint not found exception when I try to do so. The SQL is like this:
CREATE TABLE itineraries
(
id SERIAL,
itinerary_id VARCHAR(36) NOT NULL,
user_id VARCHAR(36) NOT NULL,
created_at TIMESTAMP,
version INTEGER DEFAULT 1,
update_timestamp BIGINT,
CONSTRAINT itineraries_pkey PRIMARY KEY (itinerary_id, user_id),
CONSTRAINT itineraries_user_id_fkey FOREIGN KEY (user_id) REFERENCES users (user_id)
);
CREATE UNIQUE INDEX itineraries_id_key ON itineraries (id);
CREATE TABLE subscriptions
(
subscription_id VARCHAR(36) PRIMARY KEY NOT NULL,
type VARCHAR(10) NOT NULL,
status VARCHAR(20),
last_updated TIMESTAMP NOT NULL,
itinerary_id VARCHAR(36) NOT NULL,
itinerary_db_id BIGINT,
CONSTRAINT subscriptions_it_itinerary_id_fkey FOREIGN KEY (itinerary_db_id) REFERENCES itineraries (id)
);
Which gives the following error:
Cause: org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PRIMARY KEY | UNIQUE (ID)" not found; SQL statement:
What needs to be changed about the SQL? Since to me it seems like the unique index is created before the create table query.
Unique indexes and unique constraints are different things.
You need to create a constraint and you don't need an index in H2, because unique constraints in H2 create indexes automatically.
CREATE TABLE itineraries
(
id BIGINT GENERATED BY DEFAULT AS IDENTITY CONSTRAINT itineraries_id_key UNIQUE,
…
I changed PostgreSQL-compatibility SERIAL to standard-compliant BIGINT GENERATED BY DEFAULT AS IDENTITY, because SERIAL is a legacy data type and it creates an INTEGER column, but it is referenced by subscriptions.itinerary_db_id with BIGINT data type, it isn't strictly required, but normally you should use the same data type for both columns, you can choose BIGINT, INTEGER or some other numeric type.
Usually it is more reasonable to create a primary key constraint for id column and a unique constraint for (itinerary_id, user_id), but you may have own reasons for such schema.
Also VARCHAR(36) looks like a some data type for UUID values, H2 has more efficient specialized UUID data type for this purpose.

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.

Fill in bridge table with query

I have four tables, the main purpose of these tables is to have a many to many keyword to message relationship. each keyword can have many messages and each message can have many keywords they are related together if the category id matches.
CREATE TABLE public.trigger_category
(
id integer NOT NULL DEFAULT nextval('trigger_category_id_seq'::regclass),
description text COLLATE pg_catalog."default" NOT NULL,
CONSTRAINT trigger_category_id PRIMARY KEY (id)
)
CREATE TABLE public.trigger_keyword
(
id integer NOT NULL DEFAULT nextval('trigger_keyword_id_seq'::regclass),
keyword text COLLATE pg_catalog."default" NOT NULL,
category_id bigint NOT NULL,
CONSTRAINT trigger_keyword_id PRIMARY KEY (id),
CONSTRAINT trigger_keyword_category_id_fkey FOREIGN KEY (category_id)
REFERENCES public.trigger_category (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE NO ACTION
)
CREATE TABLE public.trigger_message
(
id integer NOT NULL DEFAULT nextval('trigger_message_id_seq'::regclass),
message text COLLATE pg_catalog."default" NOT NULL,
category_id bigint NOT NULL,
CONSTRAINT trigger_message_id PRIMARY KEY (id),
CONSTRAINT trigger_message_category_id_fkey FOREIGN KEY (category_id)
REFERENCES public.trigger_category (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
)
CREATE TABLE public.trigger_keyword_trigger_message
(
trigger_keyword_id bigint NOT NULL,
trigger_message_id bigint NOT NULL,
CONSTRAINT trigger_keyword_trigger_message_trigger_keyword_id_trigger_mess PRIMARY KEY (trigger_keyword_id, trigger_message_id),
CONSTRAINT trigger_keyword_trigger_message_trigger_keyword_id_fkey FOREIGN KEY (trigger_keyword_id)
REFERENCES public.trigger_keyword (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE NO ACTION,
CONSTRAINT trigger_keyword_trigger_message_trigger_message_id_fkey FOREIGN KEY (trigger_message_id)
REFERENCES public.trigger_message (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE NO ACTION
)
I manually insert keywords in the trigger_keyword table and I manually insert messages in the trigger_message table, if they are related then they would get the same category_id
Is it possible to write a query that would automatically go through the rows and if a keyword and message have the same category_id then it would create all the appropriate rows for the bridge table trigger_keyword_trigger_message?
You could achieve this with an Oracle Merge Query.
The USING clause selects all records to insert, and the WHEN MATCHED does to inserts in the bridge table.
MERGE INTO trigger_keyword_trigger_message tktm
USING (
SELECT tk.id tk_id, tm.id tm_id
FROM
trigger_keyword tk
INNER JOIN trigger_message tm on tm.category_id = tk.category_id
) us
WHEN MATCHED THEN
INSERT (tktm.trigger_keyword_id, tktm.trigger_message_id)
VALUES (us.tk_id, us.tm_id)
;

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.

To prevent the use of duplicate Tags in a database

I would like to know how you can prevent to use of two same tags in a database table.
One said me that use two private keys in a table. However, W3Schools -website says that it is impossible.
My relational table
alt text http://files.getdropbox.com/u/175564/db/db7.png
My logical table
alt text http://files.getdropbox.com/u/175564/db/db77.png
The context of tables
alt text http://files.getdropbox.com/u/175564/db/db777.png
How can you prevent the use of duplicate tags in a question?
I have updated my NORMA model to more closely match your diagram. I can see where you've made a few mistakes, but some of them may have been due to my earlier model.
I have updated this model to prevent duplicate tags. It didn't really matter before. But since you want it, here it is (for Postgres):
START TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ WRITE;
CREATE SCHEMA so;
SET search_path TO SO,"$user",public;
CREATE DOMAIN so.HashedPassword AS
BIGINT CONSTRAINT HashedPassword_Unsigned_Chk CHECK (VALUE >= 0);
CREATE TABLE so."User"
(
USER_ID SERIAL NOT NULL,
USER_NAME CHARACTER VARYING(50) NOT NULL,
EMAIL_ADDRESS CHARACTER VARYING(256) NOT NULL,
HASHED_PASSWORD so.HashedPassword NOT NULL,
OPEN_ID CHARACTER VARYING(512),
A_MODERATOR BOOLEAN,
LOGGED_IN BOOLEAN,
HAS_BEEN_SENT_A_MODERATOR_MESSAGE BOOLEAN,
CONSTRAINT User_PK PRIMARY KEY(USER_ID)
);
CREATE TABLE so.Question
(
QUESTION_ID SERIAL NOT NULL,
TITLE CHARACTER VARYING(256) NOT NULL,
WAS_SENT_AT_TIME TIMESTAMP NOT NULL,
BODY CHARACTER VARYING NOT NULL,
USER_ID INTEGER NOT NULL,
FLAGGED_FOR_MODERATOR_REMOVAL BOOLEAN,
WAS_LAST_CHECKED_BY_MODERATOR_AT_TIME TIMESTAMP,
CONSTRAINT Question_PK PRIMARY KEY(QUESTION_ID)
);
CREATE TABLE so.Tag
(
TAG_ID SERIAL NOT NULL,
TAG_NAME CHARACTER VARYING(20) NOT NULL,
CONSTRAINT Tag_PK PRIMARY KEY(TAG_ID),
CONSTRAINT Tag_UC UNIQUE(TAG_NAME)
);
CREATE TABLE so.QuestionTaggedTag
(
QUESTION_ID INTEGER NOT NULL,
TAG_ID INTEGER NOT NULL,
CONSTRAINT QuestionTaggedTag_PK PRIMARY KEY(QUESTION_ID, TAG_ID)
);
CREATE TABLE so.Answer
(
ANSWER_ID SERIAL NOT NULL,
BODY CHARACTER VARYING NOT NULL,
USER_ID INTEGER NOT NULL,
QUESTION_ID INTEGER NOT NULL,
CONSTRAINT Answer_PK PRIMARY KEY(ANSWER_ID)
);
ALTER TABLE so.Question
ADD CONSTRAINT Question_FK FOREIGN KEY (USER_ID)
REFERENCES so."User" (USER_ID) ON DELETE RESTRICT ON UPDATE RESTRICT;
ALTER TABLE so.QuestionTaggedTag
ADD CONSTRAINT QuestionTaggedTag_FK1 FOREIGN KEY (QUESTION_ID)
REFERENCES so.Question (QUESTION_ID) ON DELETE RESTRICT ON UPDATE RESTRICT;
ALTER TABLE so.QuestionTaggedTag
ADD CONSTRAINT QuestionTaggedTag_FK2 FOREIGN KEY (TAG_ID)
REFERENCES so.Tag (TAG_ID) ON DELETE RESTRICT ON UPDATE RESTRICT;
ALTER TABLE so.Answer
ADD CONSTRAINT Answer_FK1 FOREIGN KEY (USER_ID)
REFERENCES so."User" (USER_ID) ON DELETE RESTRICT ON UPDATE RESTRICT;
ALTER TABLE so.Answer
ADD CONSTRAINT Answer_FK2 FOREIGN KEY (QUESTION_ID)
REFERENCES so.Question (QUESTION_ID) ON DELETE RESTRICT ON UPDATE RESTRICT;
COMMIT WORK;
Note that there is now a separate Tag table with TAG_ID as the primary key. TAG_NAME is a separate column with a uniqueness constraint over it, preventing duplicate tags. The QuestionTaggedTag table now has (QUESTION_ID, TAG_ID), which is also its primary key.
I hope I didn't go too far in answering this, but when I tried to write smaller answers, I kept having to untangle my earlier answers, and it seemed simpler just to post this.
You can create a unique constraint on (question_id, tag_name) in the tags table, which will ensure that the pair is unique. That would mean that the same question may not have the same tag attached more than once. However, the same tag could still apply to different questions.
You cannot create two primary keys, but you can place a uniqueness constraint on an index.
You can only have one primary key (I assume that's what you mean by "private" key), but that key can be a composite key consisting of the question-id and tag-name. In SQL, it would look like (depending on your SQL dialect):
CREATE TABLE Tags
(
question_id int,
tag_name varchar(xxx),
PRIMARY KEY (question_id, tag_name)
);
This will ensure you cannot have the same tag against the same question.
I will use PostgreSQL or Oracle.
I feel that the following is correspondent to Ken's code which is for MySQL.
CREATE TABLE Tags
(
QUESTION_ID integer FOREIGN KEY REFERENCES Questions(QUESTION_ID)
CHECK (QUESTION_ID>0),
TAG_NAME nvarchar(20) NOT NULL,
CONSTRAINT no_duplicate_tag UNIQUE (QUESTION_ID,TAG_NAME)
)
I added some extra measures to the query. For instance, CHECK (USER_ID>0) is to ensure that there is no corrupted data in the database.
I dropped out the AUTO_INCREMENT from this QUESTION_ID because I see that it would break our system, since one question cannot then have two purposely-selected tags. In other, tags would go mixed up.
I see that we need to give a name for the constraint. Its name is no_duplicate_tag in the command.