Can you make a foreign key from two tables? - sql

So, I have three tables (mail,country and client). Table COUNTRY is parent table for table MAIL, so primary key od table MAIL is postcode from MAIL and countrycode from COUNTRY. I need to add a foreign key in table CLIENT that is made of postcode and countrycode but I want postcode in that foreign key to come from COUNTRY not MAIL. I only know how to make foreign key in CLIENT from MAIL:
CREATE TABLE COUNTRY (countrycode char(3),
CONSTRAINT pk_country PRIMARY KEY(countrycode))
CREATE TABLE MAIL (postcode numeric(5), countrycode char(3),
FOREIGN KEY(countrycode) REFERENCES country(countrycode),
CONSTRAINT pk_mail PRIMARY KEY(postcode,countrycode))
CREATE TABLE CLIENT (OIB numeric(11), postcode numeric(5), countrycode char(3),
FOREIGN KEY(postcode,countrycode) REFERENCES mail(postcode,countrycode),
CONSTRAINT pk_client PRIMARY KEY(OIB))
BUT I don't want that. I want my countrycode in table CLIENT to come from COUNTRY not from MAIL.
I've tryed:
FOREIGN KEY(postcode,countrycode) REFERENCES mail(postcode) AND country(countrycode)
but it doesn't work.
Is there a way to do that?

Don't use compound primary keys. Don't replicate data across multiple tables. So, look up the country based on the post code. I would suggest:
CREATE TABLE COUNTRIES (
countryid int identity(1, 1) primary key,
countrycode char(3) unique
);
CREATE TABLE PostCodes (
postcodeid int identity(1, 1) primary key,
countryid int references countries(countryid),
postcode nvarchar(25),
unique (countryid, postcode)
);
CREATE TABLE CLIENTS (
clientId int identity(1, 1) primary key,
OIB numeric(11) unique,
postcodeid int references postcodes(postcodeid)
);
Note some things:
You can look up the country via the postal code.
Postal codes are not always numeric.
Countries change over time (e.g. East Timor, South Sudan).
Postal codes can change (e.g. 10021 on the Upper East Side of Manhattan split into 10065 and 10075, once upon a time).

No, you can't have a single foreign key reference multiple tables, but you don't need to: You can have multiple foreign keys per table, so you can just reference both.
CREATE TABLE CLIENT (
OIB numeric(11),
postcode numeric(5),
countrycode char(3),
FOREIGN KEY(postcode,countrycode) REFERENCES mail(postcode,countrycode),
FOREIGN KEY(countrycode) REFERENCES country(countrycode),
CONSTRAINT pk_client PRIMARY KEY(OIB)
)

Other posts have presented a number of arguments regarding your existing table structures, some (imho) more valid than others. My reply is based solely on your existing structures, and does not attempt to second guess whether they are “right” or “wrong”. (Yes, I have opinions on the topic, but that’s not what you were asking about.)
On the face of it, there’s no need for the FK on column CountryCode in CLIENT to go all the way to COUNTRY to “validate itself”. The FK in MAIL ensures that any CountryCode in MAIL will also be in COUNTRY; so, an FK in CLIENT to MAIL on CountryCode is just as good at validating CountryCode as an FK directly to Country.
The once exception I can see to this is if you need to have a CLIENT with a valid CountryCode, but without a valid MAIL entry. (One thing unclear from your model is whether the columns are NULLable or not.) This might be the case if a CLIENT must have a COUNTRY, but does not have to have a MAIL. To do this, I’d use two FKs: one to MAIL on both columns, and one to COUNTRY on CountryCode… with column PostalCode set to allow NULLs. This way, CountryCode will always be validated against COUNTRY, and CountryCode + PostalCode will always be validated against MAIL--but only when PostalCode is NOT NULL.
Again, whether this is “right” or “wrong” architecture ultimately depends on what problems you are trying to solve—business, performance, storage (volume), and so on.

Related

SQL - Insert valid data based on predefined values

I have two tables city and suburb. Postgresql code:
CREATE TABLE city
(
id uuid PRIMARY KEY,
name character varying NOT NULL,
CONSTRAINT city_id_key UNIQUE (id),
CONSTRAINT city_name_key UNIQUE (name)
)
CREATE TABLE suburb
(
id uuid PRIMARY KEY,
city_id uuid NOT NULL,
name character varying NOT NULL,
CONSTRAINT fk_suburb_city FOREIGN KEY (city_id) REFERENCES city (id),
CONSTRAINT suburb_id_key UNIQUE (id),
CONSTRAINT suburb_name_key UNIQUE (name)
)
I want to create a table called address for storing city + suburb pairs. Here is the DDL for address table:
CREATE TABLE address
(
id uuid NOT NULL,
city_name character varying NOT NULL,
suburb_name character varying NOT NULL
)
I want to make sure that only redundant copies of information is inserted into address. Here is an example:
I want to allow inserting into address all the city_name suburb_name pairs:
SELECT c.name AS city_name, s.name AS suburb_name
FROM city c, suburb s
WHERE c.id = s.city_id
Result:
A - B
A - C
X - Y
For the data above I want to allow all the pairs:
A - B
A - C
X - Y
But if someone wants to insert A - Y pair into address, I want the DBMS to raise an error/exception.
Questions:
Does it make sense to check a constraint like this?
If it is a valid idea, what is the best solution for this? Trigger, stored procedure, some kind of constraint?
I prefer DBSM independent solutions. I'm more interested in the basic idea of the suggested solution not in a postgresql specific solution.
Reflecting to #Yuri G's answer: I don't want any join when I read form address. I want to store real values in it not ids. Slow insert into address is not problem. Fast read is important for me. Change in city or suburb table is not a problem after insertion in address. So no need for update in address table. I just want to make sure that the data I insert into address is a valid city - suburb pair (according to city and suburb tables).
My plan is to upload city and suburb tables with lots of data and use them for validating insertions in address table. I don't want to allow my users to insert into address for example: "New York - Fatima bint Mubarak St" because Fatima bint Mubarak St. is in Abu Dhabi.
Thank you for the answers.
Let the software on the client side operate the record identifiers for City & Suburb, not their values.
And do so on the server side too:
CREATE TABLE address
(
id uuid NOT NULL,
city_id character varying NOT NULL,
suburb_id character varying NOT NULL,
CONSTRAINT fk_city FOREIGN KEY (city_id) REFERENCES city (id),
CONSTRAINT fk_suburb FOREIGN KEY (suburb_id) REFERENCES suburb (id),
)
Of course, you'll need 2 lookup operations prior to insert, basically two selects by name of the city/suburb, to retrieve these IDs (or deny operation).
Though this way you'd be keeping data integrity most simple & efficient way, I believe.

Implementing complex references without a trigger in PostgreSQL

I am creating a PostgreSQL database: Country - Province - City.
A city must belong to a country and can belong to a province.
A province must belong to a country.
A city can be capital of a country:
CREATE TABLE country (
id serial NOT NULL PRIMARY KEY,
name varchar(100) NOT NULL
);
CREATE TABLE province (
id serial NOT NULL PRIMARY KEY,
name varchar(100) NOT NULL,
country_id integer NOT NULL,
CONSTRAINT fk_province_country FOREIGN KEY (country_id) REFERENCES country(id)
);
CREATE TABLE city (
id serial NOT NULL PRIMARY KEY,
name varchar(100) NOT NULL,
province_id integer,
country_id integer,
CONSTRAINT ck_city_provinceid_xor_countryid
CHECK ((province_id is null and country_id is not null) or
(province_id is not null and country_id is null)),
CONSTRAINT fk_city_province FOREIGN KEY (province_id) REFERENCES province(id),
CONSTRAINT fk_city_country FOREIGN KEY (country_id) REFERENCES country(id)
);
CREATE TABLE public.capital (
country_id integer NOT NULL,
city_id integer NOT NULL,
CONSTRAINT pk_capital PRIMARY KEY (country_id, city_id),
CONSTRAINT fk_capital_country FOREIGN KEY (country_id) REFERENCES country(id),
CONSTRAINT fk_capital_city FOREIGN KEY (city_id) REFERENCES city(id)
);
For some (but not all) countries I will have province data, so a city will belong to a province, and the province to a country. For the rest, I shall just know that the city belongs to a country.
Issue #1: Concerning the countries that I do have province data, I was looking for a solution that will disallow a city to belong to a country and at the same time to a province of a different country.
I preferred to enforce through a check constraint that either province or country (but NOT both) are not null in city. Looks like a neat solution.
The alternative would be to keep both province and country info within the city and enforce consistency through a trigger.
Issue #2: I want to disallow that a city is a capital to a country to which it does not belong. That seems impossible without a trigger after my solution to issue #1 because there is no way to directly reference the country a city belongs to.
Maybe the alternative solution to issue #1 is better, it also simplifies future querying.
I would radically simplify your design:
CREATE TABLE country (
country_id serial PRIMARY KEY -- pk is not null automatically
,country text NOT NULL -- just use text
,capital int REFERENCES city -- simplified
);
CREATE TABLE province ( -- never use "id" as name
province_id serial PRIMARY KEY
,province text NOT NULL -- never use "name" as name
,country_id integer NOT NULL REFERENCES country -- references pk per default
);
CREATE TABLE city (
city_id serial PRIMARY KEY
,city text NOT NULL
,province_id integer NOT NULL REFERENCES province,
);
Since a country can only have one capitol, no n:m table is needed.
Never use "name" or "id" as column names. That's an anti-pattern of some ORMs. Once you join a couple of tables (which you do a lot in relational databases) you end up with multiple columns of the same non-descriptive name, causing all kinds of problems.
Just use text. No point in varchar(n). Avoid problem like this.
The PRIMARY KEY clause makes a column NOT NULL automatically. (NOT NULL sticks, even if you later remove the pk constraint.)
And most importantly:
A city only references one province in all cases. No direct reference to country. Therefore mismatches are impossible, on-disk storage is smaller and your whole design is much simpler. Queries are simpler.
For every country enter a single dummy-province with an empty string as name (''), representing the country "as a whole". (Possibly even with the same id, you could have provinces and countries draw from the same sequence ...). Do this automatically in a trigger. This trigger is optional, though.
I chose an empty string instead of NULL, so the column can still be NOT NULL and a unique index over (country_id, province) does its job. You can easily identify this province representing the whole country and deal with it as appropriate in your application.
I am using a similar design successfully in multiple instances.
I think you can actually implement all these constraints without using triggers. It does require a bit of restructuring of the data.
Start by enforcing the relationship (using foreign keys) of:
city --> province --> country
For countries with no province information, invent a province -- perhaps with the country name, perhaps some weird default name ("CountryProvince"). This allows you to have only one set of relationship between the three entities. It automatically ensures that cities and provinces are in the right country, because you would get the country through the province.
The final question is about capitals. There is a way that you can implement and enforce uniqueness with no triggers. Keep a flag in the cities table and use a unique filtered index to guarantee uniqueness:
create unique index on cities_capitalflag on cities(capitalflag) where capitalflag = 'Y';
EDIT:
You are right about the the filtered index needing the country. But that requires storing the country in that table, which, in turn, requires keeping the provinces and cities aligned with respect to country. So, this solution gets close to not needing triggers but it isn't there.

One Address Table for Many entities?

Conceptual stage question:
I have several Tables (Person, Institution, Factory) each has many kinds of Addresses (Mailing, Physical)
Is there a way to create a single Address table that contains all the addresses of all the Entities?
I'd rather not have a PersonAddress and FactoryAddress etc set of tables.
Is there another option?
The amount of data will only be several thousand addresses at most, so light in impact.
My proposal relies on the principle that one entity (person, Institution, Factory, etc) can have multiple adresses, which is usually the case (home, business, etc), and that one adress can be shared by entities of different nature:
CREATE TABLE ADDRESS
(
ID INT IDENTITY PRIMARY KEY NOT NULL,
.... (your adress fields here)
id_Person ... NULL,
id_Institution ... NULL,
id_Factory ... NULL
)
The main limit is that 2 different persons cannot share the same adress. In such a situation, you'll have to go with an additional "EntityAddress" table, like this:
CREATE TABLE ADDRESS
(
ID INT IDENTITY PRIMARY KEY NOT NULL,
.... (your adress fields here)
)
CREATE TABLE ENTITY_ADDRESS
(
ID INT IDENTITY PRIMARY KEY NOT NULL
id_Address .... NOT NULL,
id_Person .... NULL,
id_Institution ... NULL,
id_Factory .... NULL
)
The last model allows you to share for example one adress for multiple persons working in the same institution.
BUT: according to me, the 'better' solution would be to merge your different entities into one table. You will then need:
An Entity Table, made for all entities
An Entity Type table, that will contain the different entity types.
In your case you have at least 3 rows: persons, factories,
institution
If one adress per entity is enough, you could go for the address details as properties of the Entity table.
If you need multiple addresses by entity, you'll have to go with the Addresses Table with an Id_Entity as a foreign key.
If you want to share one adress among multiple entities, each entity having potentially multiple adresses (a many-to-many relation between entities and adresses), then you will need to go for the EntityAddres table in addition to the Entity and Address Tables.
Your choice between these models will depend on your needs and your businness rules.
You need to use abstraction and inheritance.
An individual and institution (I'd call it organization) are really just concrete representations of an abstract legal party.
A mailing or physical address is the concretion of an abstract address, which could also be an email address, telephone number, or web address.
A legal party can be have zero or more addresses.
An address can be belong to zero or more legal parties.
A party could use the same address for multiple roles, such as 'Home' address and 'Work' address.
If a factory is big enough, sub-facilities in the factory might have their own addresses, so you might want to consider a hierarchical relationship there. For example, each apartment in a condo has one address each. Each building in a large factory might have their own address.
create table party (
party_id identity primary key
);
create table individual (
individual_id int primary key references party(party_id),
...
);
create table organization (
organization_id int primary key references party(party_id),
...
);
create table address (
address_id identity primary key,
...
);
create table mailing_address (
address_id int primary key references address(address_id),
...
);
create table party_address (
party_id int references party(party_id),
address_id int references address(address_id),
role varchar(255), --this should really point to a role table
primary key (party_id, address_id, role)
);
create table facility (
facility_id identity primary key,
address_id int not null references address(address_id),
parent_id int null references facility(facility_id)
);
in my opinion ,you should create a pivot table to link Entity with her Address
for exampleinstitution_addresses(id, id_institution,id_address), person_addresses(id,id_person,id_address) etc...
You could very definitely do this. You could have the Address table that has an ID, then Person, Institution and Factory could all have foreign keys to the Address table.
If you need to be able to distinguish what kind of Address it is at the Address level, you could consider adding an AddressType table and having a foreign key to that on the Address table
Example:
CREATE TABLE ADDRESS
(
ID INT IDENTITY PRIMARY KEY NOT NULL,
City VARCHAR(50) NOT NULL,
State VARCHAR(2) NOT NULL,
Zip VARCHAR(10) NOT NULL,
AddressLine1 VARCHAR(200) NOT NULL,
AddressLine2 VARCHAR(200) NOT NULL,
)
CREATE TABLE Person
(
ID INT IDENTITY PRIMARY KEY NOT NULL,
AddressID INT FOREIGN KEY REFERENCES Address(ID)
)
CREATE TABLE Institution
(
ID INT IDENTITY PRIMARY KEY NOT NULL,
AddressID INT FOREIGN KEY REFERENCES Address(ID)
)
...etc
Another basic \ bullet proof system would be to organise your model around:
An Entity Table, made for all entities
An Entity Type table, that will contain the different entity types. In your case you have at least 3 rows: persons, factories, institution
If one adress per entity is enough, you could go for the address details as properties of the Entity table.
If you need multiple addresses by entity, you'll have to go with the Addresses Table with an Id_Entity as a foreign key.
If you want to share one adress among multiple entities, each entity having potentially multiple adresses (a many-to-many relation between entities and adresses), then you will need to go for the EntityAddres table in addition to the Entity and Address Tables.
EDIT: this answer was also merged with the other answer I gave here ... so I do not know if it deserves an upvote!

Designing contact/company/address tables for a database

Trying to design part of a database to hold addresses, companies and contacts. I had one design of it in which I've now got the job of 'cleaning' it due to poor design.
Bought a copy of Joe Celko's SQL Programmer Style for reference as I'm coming from a programming angle so I ended up with...
Addresses
street_1_adr varchar(80) primary key
street_2_adr varchar(80)
street_3_adr varchar(80)
zip_code varchar(10) foreign key/primary key > Regions.zip_code
With a check to ensure all addresses are unique to prevent duplicates.
Regions
city varchar(80)
region varchar(80)
zip_code varchar(10) primary key
country_nbr integer foreign key/primary key > Countries.country_nbr
With a check to ensure all regions are unique to prevent duplicates.
Countries
country_nbr integer primary key
country_nm varchar(80)
country_code char(3)
With a check to ensure that only one record exists for all the information.
Companies
company_nm varchar(80) primary key
street_1_adr varchar(80) foreign key > Addresses.street_1_adr
zip_code varchar(10) foreign key > Addresses.zip_code
Extra information
With a check to ensure that only one company with that name can exist at the address specified
Contacts
company_nm varchar(80) primary key/foreign key > Companies.company_nm
first_nm varchar(80) primary key
last_nm varchar(80) primary key
Extra information
But this means that if I want to hook, as an example, an order onto a contact I need to do it with three fields.
Does this look right or have I completetly missed the point?
Firstly, I recommend using integer values for your primary keys
(if using mysql auto_increment is a handy feature, too)
When using your PK (primary key) as a FK (foreign key) in an different table, use the same datatype and don't save names.
You seem to save the company_name in "Contacts" even though you could simply save the ID of the company and get the name via a join-select.
IN your case it is OK, since the name is the primary key (varchar), but what happens when you get the same company name twice (eg Mc Donalds has more than one location)
ERP systems deploy those kind of structures mostly as or near as:
company (id and name)
site (id, name, FK company, additional information like address)
address (mostly referenced directly in site and sometime part of site)
region + country (all of them are "basic" data and referenced by ID in address table)
company table mostly only saves the ID and Name of an company.
site table (with foreign key relation to company) gives the "company" its adresses, legal information, etc.
A couple of thoughts:
First of all, a zip code can represent multiple cities/towns in the same state. Also, one city can have multiple zip codes.
Usually you to not find an address table separate from the entity. In other words, your company table should carry the full address.
The primary keys for the tables are usually unique identifiers or auto-increment numbers separate from the actual names. That way, if a company or contact changes it's name, or a typo was entered and corrected, you do not need to cascade the change to other tables.
You may want to future proof your design by allowing for many addresses and contacts to be added to a company. What you would do is create a many to many relationship by using a junction table (http://en.wikipedia.org/wiki/Junction_table)
Company
--------------
CompanyID (PK)
...
Address
--------------
AddressID (PK)
...
CompanyAddress
--------------
CompanyID (PK)
AddressID (PK)
The CompanyAddress table will allow you to have multiple addresses for each company. You can also do the same for contacts, depending if the contact is associated with the company or the address. Below is another link that talks about how to create the many to many relationship.
http://www.tomjewett.com/dbdesign/dbdesign.php?page=manymany.php

How to design relation between tables employee,client and phone Number?

I have a relational database with a Client table, containing id, name, and address, with many phone numbers
and I have an Employee table, also containing id, name, address, etc., and also with many phone numbers.
Is it more logical to create one "Phone Number" table and link the Clients and Employees, or to create two separate "Phone Number" tables, one for Clients and one for Employees?
If I choose to create one table, can I use one foreign key for both the Client and Employee or do I have to make two foreign keys?
If I choose to make one foreign key, will I have to make the Client ids start at 1 and increment by 5, and Employee ids start at 2 and increment by 5 so the two ids will not be the same?
If I create two foreign keys will one have a value and the other allow nulls?
The solution which I would go with would be:
CREATE TABLE Employees (
employee_id INT NOT NULL,
first_name VARCHAR(30) NOT NULL,
...
CONSTRAINT PK_Employees PRIMARY KEY (employee_id)
)
CREATE TABLE Customers (
customer_id INT NOT NULL,
customer_name VARCHAR(50) NOT NULL,
...
CONSTRAINT PK_Customers PRIMARY KEY (customer_id)
)
-- This is basic, only supports U.S. numbers, and would need to be changed to
-- support international phone numbers
CREATE TABLE Phone_Numbers (
phone_number_id INT NOT NULL,
area_code CHAR(3) NOT NULL,
prefix CHAR(3) NOT NULL,
line_number CHAR(4) NOT NULL,
extension VARCHAR(10) NULL,
CONSTRAINT PK_Phone_Numbers PRIMARY KEY (phone_number_id),
CONSTRAINT UI_Phone_Numbers UNIQUE (area_code, prefix, line_number, extension)
)
CREATE TABLE Employee_Phone_Numbers (
employee_id INT NOT NULL,
phone_number_id INT NOT NULL,
CONSTRAINT PK_Employee_Phone_Numbers PRIMARY KEY (employee_id, phone_number_id)
)
CREATE TABLE Customer_Phone_Numbers (
customer_id INT NOT NULL,
phone_number_id INT NOT NULL,
CONSTRAINT PK_Customer_Phone_Numbers PRIMARY KEY (customer_id, phone_number_id)
)
Of course, the model might changed based on a lot of different things. Can an employee also be a customer? If two employees share a phone number how will you handle it on the front end when the phone number for one employee is changed? Will it change the number for the other employee as well? Warn the user and ask what they want to do?
Those last few questions don't necessarily affect how the data is ultimately modeled, but will certainly affect how the front-end is coded and what kind of stored procedures you might need to support it.
"The Right Way", allowing you to use foreign keys for everything, would be to have a fourth table phoneNumberOwner(id) and have fields client.phoneNumberOwnerId and employee.phoneNumberOwnerId; thus, each client and each employee has its own record in the phoneNumberOwner table. Then, your phoneNumbers table becomes (phoneNumberOwnerId, phoneNumber), allowing you to attach multiple phone numbers to each phoneNumberOwner record.
Maybe you can somehow justify it, but to my way of thinking it is not logical to have employees and clients in the same table. It seems you wan to do this only so that your foreign keys (in the telephone-number table) all point to the same table. This is not a good reason for combining employees and clients.
Use three tables: employees, clients, and telephone-number. In the telephone table, you can have a field that indicates employee or client. As an aside, I don't see why telephone number needs to be a foreign key: that only adds complexity with very little benefit, imo.
Unless there are special business requirements I would expect a telephone number to be an attribute of an employee or client entity and not an entity in its own right.
If it were considered an entity in its own right it would be 'all key' i.e. its identifier is the compound of its attributes and has no attributes other than its identifier. If the sub-attributes aren't stored apart then it only has one attribute i.e. the telephone number itself! Therefore, it isn't usually 'interesting' enough to be an entity in its own right and a telephone numbers table, whether superclass or subclass, is usually overkill (as I say, barring special business requirements).