SQL: Composite primary key as foreign key - sql

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.

Related

Sql creating table consisting of keys from other tables

this is probably a simple question but I am quite new to SQL and databases, so I have been following this site: https://www.postgresqltutorial.com/postgresql-foreign-key/ to try and create a table that consist of primary keys from other tables.
Here I have the structure of the database in an excel overview. With colors showing the relations. i am having problems with the One-To-Many tables. As I get the same error every time "ERROR: column "id" referenced in foreign key constraint does not exist
SQL state: 42703".
The SQL query:
DROP TABLE IF EXISTS ingredient_to_unit_relations;
DROP TABLE IF EXISTS ingrediens;
CREATE TABLE ingrediens (
id serial,
name_of_ingredient varchar(255),
price_per_unit int,
PRIMARY KEY (id)
);
CREATE TABLE ingredient_to_unit_relations (
ingredient_relation_id int GENERATED ALWAYS AS IDENTITY,
PRIMARY KEY (ingredient_relation_id),
constraint Fk_ingredient_id
FOREIGN KEY (id)
REFERENCES ingrediens (id)
);
You need to define the column in order to declare it as a foreign key:
CREATE TABLE ingredient_to_unit_relations (
ingredient_relation_id int GENERATED ALWAYS AS IDENTITY,
ingredient_id int,
PRIMARY KEY (ingredient_relation_id),
constraint Fk_ingredient_id FOREIGN KEY (ingredient_id) REFERENCES ingrediens (id)
);
I might recommend some somewhat different naming conventions (I changed the name id in the table above):
CREATE TABLE ingredients (
ingredient_id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name varchar(255),
price_per_unit int
);
CREATE TABLE ingredient_to_unit_relations (
ingredient_relation_id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
ingredient_id int,
CONSTRAINT Fk_ingredient_id FOREIGN KEY (ingredient_id) REFERENCES ingredients (ingredient_id)
);
Notes:
I am a fan of naming primary keys after the table they are in. That way, foreign keys and primary keys usually have the same name (and you can use using if you choose).
Avoid SERIAL. GENERATED ALWAYS AS IDENTITY is now recommended.
You can inline primary key constraints (as well as other constraints).
There is not generally a need to repeat the table name in a column (other than the primary key). So, name instead of name_of_ingredient.
Using int for a monetary column is suspicious. It doesn't allow smaller units. That might work for some currencies but in general I would expect a numeric/decimal type.

How to address a multiple purpose column in sql?

Scenario
I need to design a equipment movement from many sources and destinations. I have the folowing sample tables:
CREATE TABLE Area{
Id INT,
Name VARCHAR(50),
//some other fields
}
CREATE TABLE Stowage{
Id Int,
Name VARCHAR(50),
//some other fields
}
CREATE TABLE Movement{
OriginId INT,
DestinationId INT,
}
But I need some kind of movement like:
Origin : Area; Destination: Area
Origin : Area; Destination: Stowage
Origin : Stowage; Destination: Area
Origin : Stowage; Destination: Stowage
But I only have two columns and needs more than one foreign key per column.
Posible solution in mind
Create MovementArea, MovementStowage, MovementStowageArea tables and create its propertly foreigns keys.
Don't create foreigns key for the columns OriginId and DestinationId and fill it as needed.
Final Question
Is there another way to address this in sql or which of the provided solutions is most aceptable for this scenario?
Tricky. You have 4 foreign keys, so I would [naturally] create 4 foreign key columns, as in:
create table movement (
origin_area int,
origin_stowage int,
dest_area int,
dest_stowage int,
constraint fk1 foreign key origin_area references area (id),
constraint fk2 foreign key origin_stowage references stowage (id),
constraint fk3 foreign key dest_area references area (id),
constraint fk4 foreign key dest_stowage references stowage (id),
constraint chk_fk1 check (origin_area is null and origin_stowage is not null
or origin_area is not null and origin_stowage is null),
constraint chk_fk2 check (dest_area is null and dest_stowage is not null
or dest_area is not null and dest_stowage is null)
);
Now, as you see:
There are 4 nullable FK columns.
Each FK column has its corresponding FK constraint.
Also, origin_area and origin_stowage are mutually exclusive. Always one of them is null, while the other points to the other table. This is enforced by the constriaint chk_fk1.
The same can be said for dest_area and dest_stowage. Enforced by chk_fk2.
My first thought is something like this:
Create Table MovementEndpoint
(
ID Int
, Name Varchar(50)
, EndpointType Int -- Area, Stowage, etc
, EndpointDetailID Int -- FK to Area, Stowage, etc
)
Now your movements just go between endpoints, and the MovementEndpoint record lets you get to the Area or Stowage record as needed. The query logic will still be a little tricky, but no more so than your initial design requires.

Is my query correct when I set primary key for 3 columns in a table?

In my case, I have only 1 candidate may go with 1 job at the time so they are must be 2 primary key.
Then, a column is as JobApplicationId use for the table CandidateDetail as a foreign key.
Is that correct when I decide to set these 3 columns above as primary key or there are other ways to address my problem here?
CREATE TABLE Candidate(
CandidateId int identity primary key,
FullName nvarchar(50)
)
CREATE TABLE Job(
JobId int identity primary key,
JobTitle nvarchar(50)
)
CREATE TABLE JobApplication(
JobApplicationId int identity,
JobId int,
CandidateId int,
CreatedDate datetime,
primary key(JobApplicationId, JobId, CandidateId)
)
CREATE TABLE CandidateDetail(
CandidateDetailId int identity primary key,
JobApplicationId int,
[Description] nvarchar(300)
)
ALTER TABLE JobApplication ADD CONSTRAINT fk_JobApplication_Job FOREIGN KEY (JobId) REFERENCES Job(JobId)
ALTER TABLE JobApplication ADD CONSTRAINT fk_JobApplication_Candidate FOREIGN KEY (CandidateId) REFERENCES Candidate(CandidateId)
ALTER TABLE CandidateDetail ADD CONSTRAINT fk_CandidateDetail_JobApplication FOREIGN KEY (JobApplicationId) REFERENCES JobApplication(JobApplicationId)
Instead of a primary key with three columns you could just have JobApplicationId as the primary key and a unique constraint on JobId, CandidateId.
Otherwise, two rows with JobApplicationId=1, JobId=1, CandidateId=1 and JobApplicationId=2, JobId=1, CandidateId=1 would still be valid in terms of your current primary key approach, but would be invalid in terms of the business case.
From both a performance and usability perspective, a compound primary key can be a hassle and can create performance issues. Personally, I would choose JobApplicationId as the primary key (because this is an identity column and will be unique for each record). Then, if you need to constrain the table so that JobId and CandidateId are always unique (not allowing more than 1 record for any given candidate and the job they've applied for) then I would use a compound Unique Constraint.
However, I would suggest that you evaluate those requirements more closely because what if a candidate applies for the same position in a different time frame? It might stand to reason that having the same candidate applied to the same job more than once in that table might be valid data.

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

Creating Tables

Suppose you have the following database:
Person(ssn NUMERIC(9), name VARCHAR(40), gender CHAR(1)), ssn is primary key
Organization(org_code CHAR(4), budget INTEGER, org_name VARCHAR(60)), org_code is primary key
Person_Organization(ssn, org_code), both columns are the primary key.
Are the keys in the person_organization table considered foreign keys or primary keys? I am stuck on how to create this table. Have tried looking in my textbooks but cannot find information about it. I don't know if they are supposed to be foreign keys that reference the primary keys or if I should just do this
CREATE TABLE person_organization(ssn NUMERIC(9), org_code VARCHAR(60));
Any suggestions would be greatly appreciated.
Thanks.
The simple answer is that they're both.
ssn, org_code should be the primary key of person_organization.
ssn should be a foreign key back into person and org_code should by a foreign key back into organization.
To separate myself from northpole's answer I don't actually agree with the surrogate key argument in this case it doesn't seem to be needed as it won't be used anywhere else.
Unfortunately the problem with this (good) solution to the many to many relationship is that it's often needed to have two unique keys on a table, ssn, org_code and org_code, ssn and choose one as the primary key.
As you're using Oracle the create table syntax would be
create table person_organization
( ssn number(9)
, org_code varchar2(60)
, constraint person_organization_pk primary key (ssn, org_code)
, constraint person_organization_ssn_fk foreign key ( ssn )
references person ( ssn )
, constraint person_organization_oc_fk foreign key ( org_code )
references organization ( org_code )
);
In your original table creation script you had ssn as numeric(9), which should by number(9). You may want to consider not restricting the size of this data type. You also had org_code as a varchar, this should probably be a varchar2.
Tech on the Net is a really good resource for learning syntax.
I would suggest adding a unique, auto incrementing primary key to PERSON_ORGANIZATION (called something like po_id) as well as the two FOREIGN keys of ssn and org_code. You can also make those two unique if you want. From my experience, I like to have almost every table have it's own unique/auto key (unless it is a lookup table or audit table (and possibly others)).
They're both.
For the person_organization table you would have a compound primary key that consisted of the two columns. Each is separately a foreign key to another table.
For normal database design they should reference the primary keys in the other tables and these constraints enforce the validity of the data in the database.
They are foreign keys.
You've listed "both columns are the primary key" but I don't think they are.
The table does not have a primary key.
The combination of the two fields is certainly acting as a proxy for a primary key, doing things like making sure entries are uniquely identified and thus acting together as a unique identifier but that is a bit different.
I would also recommend adding a separate primary key field for consistency with the structure of others tables. As with other tables I recommend always using either id [my favorite] or tablename_id
This is the basic idea, you need to provide proper datatype for each field
CREATE TABLE Persons (
ssn int(9) NOT NULL PRIMARY KEY,
name varchar(40),
gender CHAR(1)
)
CREATE TABLE Organization (
org_code CHAR(4)NOT NULL PRIMARY KEY,
budget INTEGER,
org_name VARCHAR(60)
)
CREATE TABLE Person_Organization (
ssn int FOREIGN KEY REFERENCES Persons(ssn),
org_code CHAR FOREIGN KEY REFERENCES Organization(org_code)
)