Check Constraint in Oracle - oracle

I have been struggling to figure out a way to insert check such that it won't allow payment date to be earlier than the invoice date. basically, I have two table: invoice and payment. so I want a check constraint to enter into payment a date that is before the purchase date in invoice table. The invoice_id in Invoice table is a FK in payment table. Any help?

A check constraint can only look at columns on the table it is defined on.
You'll need to use a TRIGGER to do this.

One approach might be to duplicate the purchase date in the payment table, add a second unique constraint over the combination of invoice_id + purchase date, then modify the FK constraint to include it. Then, you can have your row-level constraint. e.g.
CREATE TABLE invoices
( invoice_id NUMBER NOT NULL
, purchase_date DATE NOT NULL
, CONSTRAINT invoice_pk PRIMARY KEY (invoice_id)
, CONSTRAINT invoice_uk UNIQUE (invoice_id, purchase_date)
);
CREATE TABLE payments
( payment_id NUMBER NOT NULL
, invoice_id NUMBER NOT NULL
, purchase_date DATE NOT NULL
, payment_date DATE NOT NULL
, CONSTRAINT payment_pk PRIMARY KEY (payment_id)
, CONSTRAINT payment_invoice_fk
FOREIGN KEY (invoice_id, purchase_date)
REFERENCES invoices (invoice_id, purchase_date)
, CONSTRAINT payment_date_ck
CHECK (payment_date >= purchase_date)
);
Downside: updating invoices.purchase_date becomes a bit tricky.

Related

ORA-02267: incompatible with the data type or collation of the referenced column

create table doctor
(
name varchar2(20)
, d_id varchar2(20)
, address varchar2(50)
, phone_number number(10)
, qualification varchar2(20)
, gender varchar2(20)
, constraint pk_doctor primary key(d_id)
)
;
create table room
(
room_id varchar2(5)
, room_type varchar2(20)
, constraint pk_room primary key(room_id)
)
;
create table patient
(
p_id varchar2(10)
, p_name varchar2(20)
, p_age number(3)
, p_gender varchar2(10)
, address varchar2(50)
, date_admission date
, phone_number number(10)
, room_id varchar2(5)
, constraint pk_patient primary key(p_id)
, constraint fk_p1 foreign key(room_id) references room
)
;
create table bill
(
bill_no varchar2(10)
, bill_date date
, p_id varchar2(10)
, p_name varchar2(20)
, p_age number(3)
, p_gender varchar2(10)
, date_admission date
, date_discharge date
, room_charges number(10)
, pathology_fees number(10)
, d_fees number(10)
, miscellaneous number(10)
, total_amount number(10)
, constraint pk_bill primary key(bill_no)
, constraint fk_b1 foreign key(p_id) references patient
, constraint fk_b2 foreign key(p_name) references patient
, constraint fk_b3 foreign key(p_age) references patient
, constraint fk_b4 foreign key(p_gender) references patient
, constraint fk_b5 foreign key(date_admission) references patient
)
;
Error starting at line : 15 in command -
create table bill
(
bill_no varchar2(10)
, bill_date date
, p_id varchar2(10)
, p_name varchar2(20)
, p_age number(3)
, p_gender varchar2(10)
, date_admission date
, date_discharge date
, room_charges number(10)
, pathology_fees number(10)
, d_fees number(10)
, miscellaneous number(10)
, total_amount number(10)
, constraint pk_bill primary key(bill_no)
, constraint fk_b1 foreign key(p_id) references patient
, constraint fk_b2 foreign key(p_name) references patient
, constraint fk_b3 foreign key(p_age) references patient
, constraint fk_b4 foreign key(p_gender) references patient
, constraint fk_b5 foreign key(date_admission) references patient
)
Error report -
ORA-02267: column type incompatible with referenced column type
02267. 00000 - "column type incompatible with referenced column type"
*Cause: The data type or collation of the referencing column was
incompatible with the data type or collation of the referenced
column.
*Action: Select a compatible data type for the referencing column.
Also, the collation of a character column in a foreign key must
match the collation of the corresponding column in the primary
key.
I understand what the error is trying to tell but my datatypes are same in both tables but still, it is showing the error??
Please tell me where I made a mistake. I would be very grateful.
From the documentation:
If you identify only the parent table or view and omit the column name, then the foreign key automatically references the primary key of the parent table or view.
So you're effectively actually doing:
, constraint fk_b1 foreign key(p_id) references patient
, constraint fk_b2 foreign key(p_name) references patient(p_id)
, constraint fk_b3 foreign key(p_age) references patient(p_id)
, constraint fk_b4 foreign key(p_gender) references patient(p_id)
, constraint fk_b5 foreign key(date_admission) references patient(p_id)
which obviously isn't what you want, and explains the error you get.
You need to specify the matching non-PK columns:
, constraint fk_b1 foreign key(p_id) references patient
, constraint fk_b2 foreign key(p_name) references patient(p_name)
, constraint fk_b3 foreign key(p_age) references patient(p_age)
, constraint fk_b4 foreign key(p_gender) references patient(p_gender)
, constraint fk_b5 foreign key(date_admission) references patient(date_admission)
However, this will not get
ORA-02270: no matching unique or primary key for this column-list
because those four columns aren't suitable targets; none can really be unique, at least safely. And it wouldn't allow data to change - name and gender could, but age will, for example. These may be recording the patient's status on admission though, so then those wouldn't change, other than to correct mistakes.
It doesn't really make sense to duplicate/denormalise the data. You can just have the PK reference and join to the main table as needed to get the other information.
db<>fiddle
Splitting patient into a table to identify an individual (with date of birth rather than age) and a separate table that records each stay for that patient - with room and admission/discharge dates, for example - might make more sense. Only recording the discharge date on the bill seems odd.
The problem you're facing comes from bad data model which is needed for some rework.
The "bill" table contains p_name, p_age, p_gender, and date_admission columns. Same columns exist in the "patient" table, so you don't need them in the "bill". Just remove the columns from the "bill" definition and it'll be fine.
Whenever you need to get patient's name for the bill, you can always get it using p_id:
select p.p_name, p.p_age
from patient p
join bill b
on b.p_id, p.p_id
where b.bill_no = 'some_bill_no'
And keeping patient's age will lead to an engineering overhead because you'll have to update this field constantly once per year. I'd suggest you to store patient's birth_date and calculate his/her age when needed.
Of course when it is not a homework with gives table structure

MODIFY or ADD to add NOT NULL constraint to a column? Oracle sql

ORDERS table in the Oracle Database:
ORDERS
ORDER_ID NOT NULL NUMBER(4)
ORDATE_DATE DATE
CUSTOMER_ID NUMBER(3)
ORDER_TOTAL NUMBER(7,2)
The ORDERS table contains data and all orders have been assigned a customer ID. I'm trying to add a NOT NULL constraint to the CUSTOMER_ID column. Would I use MODIFY CONSTRAINT or ADD CONSTRAINT? I was told you have to drop the constraint and ADD the new one, but if there is no existing constraint to Customer ID number, would it be MODIFY?
alter table orders modify customer_id not null;
Just MODIFY the column:
alter table orders modify customer_id not null;
Alternatively, you could add an [overkill] constraint in the form:
alter table orders add constraint nn1 check (customer_id is not null);
Just use the first form.
As a side note, some databases (such as Oracle) consider those two constraint different and somewhat separate: the former is a column constraint, while the latter is a table constraint. Oracle keeps track in case you drop one, while the other is still in effect.

how to create a foreign key in Oracle

How to link the MgrId in ManagerProject to EmpId in the Employee table ?
This is wat I tried :
CREATE TABLE Employee(EmpId varchar2(5),
EmpName varchar2(25),
DeptId varchar2(3),
Salary Number(8),
Constraint PK_addn primary key (EmpId, DeptId),
Constraint fk_Department foreign key (DeptId) references Department (DeptId));
But the second table failed to be created :
CREATE TABLE ManagerProject(ProjId varchar2(4),
MgrId varchar2(5),
StartDate Date,
EndDate Date,
Constraint fk_managerproject foreign key (MgrId) references Employee (EmpId),
Constraint PK_Managerproject Primary key(ProjId, MgrId, StartDate));
It displays
ORA-02270: no matching unique or primary key for this column-list
The error message says that you are trying to create a FK referencing a column on which there is no Unique or Primary Key constraint.
Assuming that you don't want to add the column DeptId to ManagerProject, you need to add a unique key on employee:
alter table Employee add constraint empId_UK unique ( empId)
But this strongly depends on what your schema should be.
If you want to add the column DeptId to ManagerProject, you will need to edit your FK to both use EmpId and DeptId in referencing employee.

one attribute referencing, attributes in two different tables

I have 4 tables
customer: CustomerID - primary key, name
Magazine: name - primary key, cost, noofissues
Newspaper: name - primary key, cost, noofissues
subscription: custID - references CustomerID of Customer, name, startdate, enddate
In the above, can I reference the name from subscription table to reference name from Magazine and name from Newspaper?
I have created the tables Customer, Newspaper and Magazine. I only need to create Subscription.
Can you do something like this?
CREATE TABLE subscription (
custID INT
CONSTRAINT subscription__custid__fk REFERENCES Customer( CustomerId ),
name VARCHAR2(50)
CONSTRAINT subscription__mag_name__fk REFERENCES Magazine( Name )
CONSTRAINT subscription__news_name__fk REFERENCES Newspaper( Name ),
startdate DATE
CONSTRAINT subscription__startdate__nn NOT NULL,
enddate DATE
);
Yes, you can and you will have two foreign keys on the same column pointing to different tables but if the value in the column is non-null then it will expect there to be a matching name in both the magazines table and the newspapers table - which is probably not what you are after.
Can you have a foreign key that asks can the value be in either exclusively in this table or that table (but not in both)? No.
But you can re-factor your database so you merge the newspapers and magazines tables into a single table (which you can then easily reference); like this:
CREATE TABLE customer (
CustomerID INT
CONSTRAINT customer__CustomerId__pk PRIMARY KEY,
name VARCHAR2(50)
CONSTRAINT customer__name__nn NOT NULL
);
CREATE TABLE Publications (
id INT
CONSTRAINT publications__id__pk PRIMARY KEY,
name VARCHAR2(50)
CONSTRAINT publications__name__nn NOT NULL,
cost NUMBER(6,2)
CONSTRAINT publications__cost__chk CHECK ( cost >= 0 ),
noofissues INT,
type CHAR(1),
CONSTRAINT publications__type__chk CHECK ( type IN ( 'M', 'N' ) )
);
CREATE TABLE subscription (
custID INT
CONSTRAINT subscription__custid__fk REFERENCES Customer( CustomerId ),
pubID INT
CONSTRAINT subscription__pubid__fk REFERENCES Publications( Id ),
startdate DATE
CONSTRAINT subscription__startdate__nn NOT NULL,
enddate DATE
);
If you are asking whether you can create a foreign key constraint on subscription that references either the newspaper table or the magazine table, the answer is no, you cannot. A foreign key must reference exactly one primary key.
Since magazine and newspaper have the same set of attributes, the simple option is to combine them into a single periodical table with an additional periodical_type column to indicate whether it is a magazine or a newspaper. You could then create your foreign key to the periodical table.
Although it probably won't make sense in this particular example, you could also have separate columns in subscription for magazine_name and newspaper_name and create separate foreign key constraints on those columns along with a check constraint that ensured that exactly one of the values was non-NULL. That might make sense if the two different parent tables had radically different attributes.
Not related to your question but as a general bit of advice, I wouldn't use the name as the primary key. In addition to being rather long, names tend to change over time and names aren't necessarily unique. I would use a different attribute for the key, potentially a synthetic primary key generated from a sequence.

Employee/History - Part of composite key as foreign key

I've got 2 entities:
1) EMPLOYEES (Parent)
CREATE TABLE EMPLOYEES (
employee_id NUMBER (3) NOT NULL,
first_name VARCHAR (20) NOT NULL,
last_name VARCHAR (20) NOT NULL,
job_title VARCHAR (20) NOT NULL,
employee_type VARCHAR (1) NOT NULL,
salary NUMBER (5),
hourly_pay NUMBER (5,2),
bonus_pay NUMBER (5,2),
CONSTRAINT employee_pk PRIMARY KEY(employee_id));
2) EMPLOYEE_HISTORY (Child)
CREATE TABLE EMPLOYEE_HISTORY (
start_date DATE NOT NULL,
employee_id NUMBER (3) NOT NULL,
end_date DATE,
job_title VARCHAR (10) NOT NULL,
hourly_rate NUMBER (5,2) NOT NULL,
CONSTRAINT employee_history_pk PRIMARY KEY(start_date, employee_id));
I'm trying to create:
ALTER TABLE employee_history
ADD CONSTRAINT employee_history_fk
FOREIGN KEY (employee_id)
REFERENCES employee_history(employee_id);
When I do this, I get an error
ORA-02270: no matching unique or primary key for this column-list
My guess is that I cannot create the constraint on just employee_id because I have a composite key in my child table. I understand when an employee gets put into the database, the parent table is filled out and the "start date" should be filled out along with everything else. However, I do not understand how this would work if I had start_date in my parent table as well. I would be able to create my constraint, yes, but how will I be able to keep a record of changes in start_date if my start_date was inputted at the time of when the employee was entered into the database.I thought about using job_title as a primary key instead of start_date because it's present in both tables, but what happens when an employee gets promoted and demoted again? Won't a duplicate value constraint come up when the same employee_id and job_title is getting inserted?
Your references clause needs to reference the parent table. Not the child table
ALTER TABLE employee_history
ADD CONSTRAINT employee_history_fk
FOREIGN KEY (employee_id)
REFERENCES employee(employee_id); -- employee not employee_history
The SQL you posted is trying to create a self-referential foreign key where employee_history is both the parent and the child. That doesn't make sense in this case.

Resources