'Nested' foreign key relationship - sql

A Product has a code (and other attributes). A Product is available in various countries, with a different name in each. A Packaged Product is a product in a particular pack size.
An invoice detail line will hold a package_code and a country_code. The permissible countries for a PackagedProduct are restricted by the contents of the ProductCountry table, i.e. the PackagedProduct is only available in a given Country if the Product is available in that Country.
Here's my approach (Country and InvoiceDetail tables omitted):
create table Product (
product_code varchar(15),
strength varchar(10),
-- other Product attributes
constraint pk_Product primary key (product_code)
);
create table ProductCountry (
product_code varchar(15),
country_code char(2),
local_name varchar(100),
constraint pk_ProductCountry primary key (product_code, country_code),
foreign key (product_code) references Product(product_code),
foreign key (country_code) references Country(country_code)
);
create table PackagedProduct (
package_code varchar(20),
product_code varchar(15),
pack_size int,
constraint pk_PackagedProduct primary key (package_code),
foreign key (product_code) references Product(product_code)
);
My problem is that this design does not restrict Invoice Detail records to valid combinations of PackagedProduct and Country.
Can we improve this design?

Related

SQL: Composite primary key as foreign key

I'm creating a database in postgres about NY counties on their education aid amount and criminality rates ranging from 2010 to 2019, these are the two tables
CREATE TABLE NYAidCrimeTimeSeries
(
County VARCHAR(50),
Year int,
AidAmount int,
Population int,
JailPopulation int,
CrimesReported int,
PRIMARY KEY (County, year)
)
CREATE TABLE NYAidCrimeMean
(
County VARCHAR(50),
AidAmount_mean int,
Population_mean int,
JailPopulation_mean int,
CrimesReported_mean int,
AidPerCap int,
CrimesPerCap int,
FOREIGN KEY (County) REFERENCES nyaidcrimetimeseries (County)
)
Would this be possible? Having a composite primary key as a foreign key? If not, what direction should I go try?
The purpose of a foreign key is to guarantee that one or more keys in one table have a corresponding row in another table. The "a" in this context is singular. And, in general, foreign keys should be using primary keys. Any unique key is allowed, but primary keys are strongly recommended.
Your data model is just begging for a counties table. At the very least:
create table counties (
county_id int generated always as identity primary key,
name varchar(255)
);
The county_id can then be a foreign key in both the other tables. Well, in fact, perhaps the summary statistics could also be columns in counties.
Hmmmm . . . It is possible that you just have the foreign key definitions in the wrong place. You want county to be the primary key of NYAidCrimeMean and then for NYAidCrimeTimeSeries to reference that table.
If you do take this approach, I would suggest renaming NYAidCrimeMean because the table name does not suggest that it is one row per county. At least to those not familiar with your domain.

Foreign key references mutiple columns in the database

When I get my EER converted to the relational schema I got this table which has a composite primary key like following,
CREATE TABLE branch
(
branch_number varchar(20),
b_code varchar(20),
name varchar(20),
address varchar(20),
--CONSTRAINTS
CONSTRAINT pk_branch PRIMARY KEY(branch_number,b_code),
CONSTRAINT fk_branch FOREIGN KEY(b_code) REFERENCES bank(b_code)
)
This above table is a weak entity and therefore it's having a key combination as a primary key. (due to the identifying relationship)
Then this branch table is in a one-to-many relation with the following table
CREATE TABLE account
(
account_no varchar(20),
balance float,
branch_number varchar(20),
b_code varchar(20),
a_code varchar(20),
--CONSTRAINTS
CONSTRAINT pk_account PRIMARY KEY(account_no),
CONSTRAINT fk_account_1 FOREIGN KEY(b_code) REFERENCES branch(b_code),
CONSTRAINT fk_account_2 FOREIGN KEY(branch_number) REFERENCES branch(branch_number),
CONSTRAINT fk_account_3 FOREIGN KEY(a_code) REFERENCES account_type(a_code)
)
How do I make the relation between these two tables? How to implement the foreign key for the account table with branch when I have one column referencing to two other columns?
Your fk_account_1 will fail when you try to compile it because b_code is not a primary or unique key on table branch. Same problem with fk_account_2, because branch_number is not primary or unique on branch.
The primary key on branch is (branch_number,b_code) so your foreign key should be
CONSTRAINT FK_ACCOUNT_1
FOREIGN KEY (BRANCH, B_CODE)
REFERENCES BRANCH (BRANCH, B_CODE);

Is it possible to reference a foreign key to parent table?

CREATE TABLE Product
(
"Product_id" int,
"Stock_quantity" int,
"Product_name" varchar(50),
"Model" varchar(50),
"Average_rating" float(3),
"RAM" int,
"Color" varchar(20),
"Price" float(10),
PRIMARY KEY ("Product_id")
);
CREATE TABLE Sale
(
"Sale_id" int,
"Sale_date" date,
"Employee_id" int,
"Customer_id" int,
"Product_id" int,
"Product_quantity" int,
"Rating" int,
PRIMARY KEY ("Sale_id"),
FOREIGN KEY("Employee_id") REFERENCES Employee("Employee_id") ,
FOREIGN KEY("Customer_id") REFERENCES Customer("Customer_id") ,
FOREIGN KEY("Product_id") REFERENCES Product("Product_id")
);
CREATE TABLE Computer
(
"Type" varchar(10),
"Processor" varchar(20),
"Monitor_size" int
) inherits(Product);
CREATE TABLE Mobile
(
"Os" varchar(30),
"Screen_size" int
) inherits(Product);
Here is my tables while insertion of sale rows I get this error.
ERROR: insert or update on table "sale" violates foreign key constraint "PK_Product_id"
SQL state: 23503
Detail: Key (Product_id)=(12) is not present in table "product".
It says there is no presence of the row, but I can see them when I view the table:
I inserted every product as computer or mobile not as product.
You are stumbling over one of the many quirks with inheritance: There are no “global” constraints on an inheritance hierarchy, consequently you cannot have a foreign key to a inheritance hierarchy.
The primary key you defined on product does not extend to computer.
Even though a SELECT on product will append the results for the inheritance children as well, these are not part of the table parent and consequently cannot be used as target for the foreign key. The foreign key is between sale and product only, the inheritance children are excluded.
There is no proper way to do this with inheritance.
It is better for computer not to be an inheritance child of product, but to have a foreign key to product (with a unique constraint on it), so that the data for a computer object will be split between the two tables. This is somewhat inconvenient for queries, but you can have a foreign key that way.
Here is an example:
CREATE TABLE product (
product_id integer PRIMARY KEY,
prodname text NOT NULL
);
CREATE TABLE computer (
product_id integer PRIMARY KEY,
processor text NOT NULL,
FOREIGN KEY (product_id) REFERENCES product(product_id)
);
CREATE TABLE sale (
sale_id integer PRIMARY KEY,
product_id integer NOT NULL REFERENCES product(product_id)
);
Now there is no direct foreign key reference from sale to computer, but since the product_id of computer and product is identical, you can always find the unique computer for a sale if it exists:
SELECT p.prodname, c.processor
FROM sale s
JOIN product p USING (product_id)
LEFT JOIN computer c USING (product_id)
WHERE sale_id = 42;
If you have more product “subtypes”, you can add more left joins.

record addition in sql

I am making an inventory management system.
I wanted to enter products which were bought by the customer in product_sale table.
create table product_sale(
recipt# int primary key identity(1001,1),
product_id int,
product_name varchar(50),
brand_name varchar(50),
prchase_date date,
purchase_time time,
gross_price int,
discount_price int,
retail_price int,
quantity int,
)
the problem is that one customer can buy several items, and they will be inserted in multiple rows but as recipt# is primary key,so the other record will be added on another recipt#
so what should I do either should remove primary key contraint from recipt#
or something more preferred....
--Product info--
CREATE TABLE PRODUCT
(
PRODUCTID INT NOT NULL,
PRODUCT_NAME VARCHAR2(20) NOT NULL,
CONSTRAINT PRODUCT_PK PRIMARY KEY(PRODUCT_ID)
);
--sales info--
CREATE TABLE SALES
(
RECIPT_ID SMALLINT DEFAULT (0) NOT NULL,
PRODUCTID INT DEFAULT (0) NULL,
CONSTRAINT SALES_PK PRIMARY KEY(RECIPT_ID),
CONSTRAINT SALES_PRODUCT_FK FOREIGN KEY(PRODUCTID) REFERENCES PRODUCT(PRODUCT_NAME)
);
Edit[1]: since you elaborated more, I would always want to have two different tables because you want a list of your inventory on one table then on the second table you would want the sales. This helps keep all the clutter away from the view you wish to seek.
Mu suggestion is you should denormalize this table by separating order item and receipt ID. Think this is classic example and you can find lots of examples in web.
For example http://www.tomjewett.com/dbdesign/dbdesign.php?page=manymany.php

oracle table creation

The following code gives me ERROR at line 3: ORA-00907: missing right parenthesis:
CREATE TABLE ORGANISATION(
ORG_REF VARCHAR(5),
POSTCODE VARCHAR(10) FOREIGN KEY,
TELEPHONE NUMBER FOREIGN KEY,
DESCRIPTION VARCHAR(30),
AGENCY_ID VARCHAR(5));
Line 3 code is very annoying because looking at the line there are no spelling mistakes and everything is in the right place.
That's not how you define a foreign key. A foreign key must know how to find it's partner.
Read here: http://www.techonthenet.com/oracle/foreign_keys/foreign_keys.php
Foreign key definition goes something like this:
CREATE TABLE ORGANISATION(
ORG_REF VARCHAR(5),
POSTCODE VARCHAR(10), --THIS WILL BE FOREIGN KEY
TELEPHONE NUMBER, --2nd FOREIGN KEY
DESCRIPTION VARCHAR(30),
AGENCY_ID VARCHAR(5),
FOREIGN KEY FK_POSTCODE
REFERENCES other_table (post_code),
FOREIGN KEY FK_TELEPHONE
REFERENCES other_table2 (phone)
);
UPDATE:
Additional Recommended Reading: http://mattgemmell.com/2008/12/08/what-have-you-tried/
Where to start?
You should be using varchar2 not varchar. Although they're currently identical the future behaviour of varchar is not guaranteed
Telephone number as a numeric field? A lot of phone numbers start with a 0. You're losing this. If you ever want to display it nicely you have to do some funky string manipulation on exit.
If your IDs are numbers then you should store them as a number.
There is rarely a situation where a table should not have a primary key.
A foreign key is designed to enforce referential integrity in the database. There should therefore be one or two more tables in this schema as a minimum.
A typical situation might be like this, which assumes that the same postcode,phone combination exists in agency.
CREATE TABLE ORGANISATION(
ORG_REF VARCHAR2(5),
POSTCODE VARCHAR2(10) ,
TELEPHONE VARCHAR2(50),
DESCRIPTION VARCHAR(30),
AGENCY_ID VARCHAR(5),
CONSTRAINT PK_ORGANISATION PRIMARY KEY ( org_ref ),
CONSTRAINT FX_ORGANISATION FOREIGN KEY
REFERENCES SOME_OTHER_TABLE(POSTCODE,PHONE)
);
If it were just a single column and not 2 you could reference it inline, something like the following:
create table organisation (
org_ref number(16) not null
, phone varchar2(5) not null constraint fk_organisation
references agency ( phone )
, constraint pk_organisation primary key ( org_ref )
);
However, I doubt very much that this'll work. A foreign key must reference a unique constraint. So, judging by your comments you must have a table agency with a unique constraint or primary key on phone, postcode.
I suspect your data-model is flawed; it sounds as though organisation inherits from agency.
I would remove the phone and postcode from agency and just do a join to get that information, if you're currently looking at the agency table:
select a.*, o.postcode, o.phone
from agency a
join organisation o
on a.agency_id = o.agency_id
where a.id = 12345
Further reading:
Examples
Documentation
CREATE TABLE ORGANISATION(
ORG_REF VARCHAR(5),
POSTCODE VARCHAR(10),
TELEPHONE NUMBER,
DESCRIPTION VARCHAR(30),
AGENCY_ID VARCHAR(5),
constraint pcodefk foreign key(POSTCODE) references postalcodetable(POSTALCODE),
constraint telefk foreign key(TELEPHONE) references telephonenumbers(TELEPHONE));