Master child table with foreign key and primary key - sql

I am designing a SQL Server database. For the following cases, which is the correct approach?
First approach:
Second approach:

This is how I'd do it if you want a user to only be part of a single user type:
create table UserTypes (User_Type_Id int identity(1,1) primary key
,UserDescription varchar(256))
create table Users (UserId int identity (1,1) primary key
,User_Type_Id int foreign key references UserTypes (User_Type_Id)
,FirstName varchar(64)
,LastName varchar(64)
,Email varchar(256))
Some comments...
This uses the IDENTITY property which auto increments in this example from 1 to 2+ billion
I would split the name into first and last, so you don't have to split them later and cause headaches with name normalization
I would avoid using reserved words like DESCRIPTION and USER_ID which are used by SQL Server and thus would need to be enclosed in brackets.
If you want a user to be part of multiple user types, then perhaps:
create table UserTypes (User_Type_Id int identity(1,1) primary key
,UserDescription varchar(256))
create table Users (UserId int identity (1,1) not null
,User_Type_Id int foreign key references UserTypes (User_Type_Id) not null
,FirstName varchar(64)
,LastName varchar(64)
,Email varchar(256))
alter table Users
add constraint PK_UserID_UserType PRIMARY KEY CLUSTERED(UserId, User_Type_Id)

Without knowing anything about your project, I'm going to assume the first one is wrong. First, you can't have two primary keys on a table. A primary key, or a unique clustered index, organizes the physical order of the table. You can't organize it in two ways. You can PK two columns into a composite key no problem. Secondly, even if you changed the user type ID to unique instead of a PK, that means only 1 user could exist with each type ID. As soon as you tried to make another user of the same type id, you would violate that unique constraint.
The 2nd model looks better. It assumes that there cannot be the same person with the same role/user type. But the typeID in the user table should be a FK instead of a PK.

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.

hibernate there can be only one auto column and it must be defined as a key

I know that this has been asked many times but the answers are not clear.
Why Hibernate does not allow this.
I was able to create a table with columns by had, where the ID column was part of keys making the PK and Auto_increment.
The below block keeps throwing:
hibernate there can be only one auto column and it must be defined as
a key
create table rep (
ID integer not null AUTO_INCREMENT,
RepID integer not null,
QNointeger not null,
CNo integer not null,
Value varchar(255),
primary key (RepID , QNo, ID, CNo)
) engine=InnoDB;
This is a MySQL error. I am guessing that you really want:
create table rep (
ID int not null AUTO_INCREMENT PRIMARY KEY,
RespondentID int not null,
QuestionNo int not null,
ChoiceNo int not null,
Value varchar(255),
unique (RespondentID, QuestionNo, ChoiceNo)
) engine=InnoDB;
That is, the id is the primary key for the table. At the same time, the triplet of other columns is guaranteed to be unique.
Note that the column names in the unique constraint need to match the column names in the table.
You can map that if you want. Also see here for an example: https://vladmihalcea.com/how-to-map-a-composite-identifier-using-an-automatically-generatedvalue-with-jpa-and-hibernate/
Anyway, I would recommend you don't do that. It's just unnecessary to introduce a auto increment column next to other columns that are unique already. If the other columns together aren't unique, why not just use the auto increment column alone for the primary key? In relational modelling, a primary key should be minimal which is not the case if you have a auto increment column + anything else. If you want to ensure that a question is not answered multiple times, use a unique constraint to enforce that. Either that unique constraint is at the same time also your primary key, or you introduce a surrogate i.e. the auto increment column for the primary key purpose.

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.

SQL Foreign key issue with 2 parent tables

I have have 2 tables User and Group.
I have a table Attributes shared by user and group with columns:
attributeName.
AttributeValue.
ObjectID.
ObjectID points to either the primary key of user or the primary key of Group.
I have added a foreign constraint with Cascade on Delete in order to delete automatically the attributes when user or a group is deleted.
The problem now is when I insert an attribute for the user, I have a foreign key constraint because the group does not exist.
How should I proceed?
You have basically 3 options:
Keep your current design, but replace Attribute.ObjectID with UserID and GroupID, attach a separate FK to each of them (one towards Group and the other towards User) and allow either to be NULL. You'd also want a CHECK constraint to ensure not both of them are NULL.
Split Attribute table to UserAttribute and GroupAttribute, thus separating each foreign key into its own table.
Use inheritance, like this:
The solution (1) is highly dependent on how your DBMS handles UNIQUE on NULLs and both (1) and (2) allow the same AttributeName to be used for two different attributes, one for user an the other for group.
As you have discovered you can not have one column as foreign key to two different tables. You can't add a attribute for a user when it does not exist a group with the same id. And you can of course not know if the attribute is for a user or a group.
From comments you also mentioned a m:m relation between user and group so I would suggest the following.
create table [User]
(
UserID int identity primary key,
Name varchar(50) not null
)
go
create table [Group]
(
GroupID int identity primary key,
Name varchar(50) not null
)
go
create table UserGroup
(
UserID int not null references [User](UserID),
GroupID int not null references [Group](GroupID),
primary key (UserID, GroupID)
)
go
create table UserAttribute
(
UserAttributeID int identity primary key,
Name varchar(50) not null,
Value varchar(50) not null,
UserID int not null references [User](UserID) on delete cascade
)
go
create table GroupAttribute
(
GroupAttributeID int identity primary key,
Name varchar(50) not null,
Value varchar(50) not null,
GroupID int not null references [Group](GroupID) on delete cascade
)
Note: The use of an attribute table should be for attributes you don't know before hand. All the stuff you know will be attributes should be fields in the actual table instead. Reserve the use of the attributes for customer defined attributes.
I think you should allow NULL values for this foreign key field ObjectId, so that you can insert any row with ObjectId = null that not referencing any user or group.
For a better design you should remove this ObjectId column, add a new column AttributeId to the two tables User and Group.

Foreign Key Problem in SQL

I just wanna ask coz I created a table with a column with two primary keys as shown below:
CREATE TABLE Parent(
Mother varchar(50) NOT NULL,
Father varchar(50) NOT NULL,
PRIMARY KEY(Mother, Father),
Organization varchar(50) NOT NULL
);
Then I am trying to create another table which will have a foreign key to the table shown above:
CREATE TABLE Child(
Name varchar(50) NOT NULL PRIMARY KEY,
Child_Mother varchar(50) REFERENCES Parent(Mother),
Child_Father varchar(50) REFERENCES Parent(Father),
Sibling varchar(50) NOT NULL
);
So I basically am trying to references two columns on the Child Table to a single column from the Parent table with two primary keys. Is this even possible? Thank you very much! :)
*Sample Tables only. But the actual are similar to this.
You haven't created a "table with a column with two primary keys", you've created one with a single composite primary key.
And what you are doing is indeed possible but not in the way you are doing it.
The problem is that you can have two Parent rows with the same Mother and different Father. But your foreign key relationship on Child_Mother will need a single instance of Mother in the Parent table otherwise it won't know which row it's referring to.
You can either have a combined column in the child which references the conbined primary key as you have it, or you can separate the Parent rows.
By that, I mean a parent table wouldn't normally put two people in a single row, it would normally have one row per person and the child would simply reference two separate rows in the Parent table, one for the mother and one for the father.
In terms of your actual tables:
create table Subject(
Subject_ID varchar(50) NOT NULL,
Subject_Description varchar(50) NOT NULL,
PRIMARY KEY(Subject_ID, Subject_Description),
Subject_SID int references Student(Student_ID),
Unit varchar(50) NOT NULL
)
This is not actually normalised since the subject description almost invariable depends on the subject ID. I think the primary key should probably just be on subject ID. By all means have another index on subject description but it shouldn't be part of the primary key.
I'm still not sure that you understand what I'm saying so I'll try to clarify. The line:
PRIMARY KEY(Subject_ID, Subject_Description)
does not make two primary keys, it makes a single primary key made up of the two columns (id and description). Because of that, it is possible to have two rows with the same ID provided that the description is different.
So a foreign key reference to the ID column is not possible. Let's say you had two rows:
ID Subject Other
-- ------- ---------
7 Maths blah blah
7 Physics yada yada
and then tried to add to the schedule table a row with Schedule_SID set to 7. That will choke because the DBMS wouldn't know which 7 you're referring to. So it shouldn't even let you set up that foreign key constraint because the target column isn't unique.
Try this one -
CREATE TABLE Child(
Name varchar(50) NOT NULL PRIMARY KEY,
Child_Mother varchar(50),
Child_Father varchar(50),
Sibling varchar(50) NOT NULL,
Foreign Key (Child_Mother, Child_Father) references Parent(Mother, Father)
);
Since you are creating a primary key which consists of two columns, you need to refer to them together.
What paxdiablo said is true. You are creating a composite primary key which consists of two columns. I would suggest you to use a Surrogate Primary Key.
Edit
CREATE TABLE Child(
id bigint NOT NULL auto_increment, // an "id" column
Name varchar(50) NOT NULL unique,
age int not null,
primary key (id) // "id" column is being declared as surrogate primary key
);
Second Edit
create table Subject(
id int not null,
Subject_ID varchar(50) NOT NULL,
Subject_Description varchar(50) NOT NULL,
PRIMARY KEY(id),
Subject_SID int references Student(Student_ID),
Unit varchar(50) NOT NULL
);
ALTER TABLE Subject ADD CONSTRAINT subject_unique UNIQUE (Subject_id, Subject_Description);
create table Schedule(
Day_MTWTHF varchar(50) NOT NULL,
TIME_HH varchar(50) NOT NULL,
subject_id int not null, -- my newly added column
Schedule_SID int references Student(Student_ID),
foreign key(subject_id) references Subject(id)
);
I'd say your choice of primary key is poor for the Parent table. It looks like you're using names. What happens if you've got two seperate couples whose names match up? John & Jane Smith with Alice and Bob for children, under your design, would also be the same John & Jane Smith from the next town over who've got Charlie and Dolores for kids, even though in reality they're completely unrelated.
Names are invariably a bad choice for keys, as names can be repeated. Using something guaranteed to be unique, such as an auto-incrementing integer, would be far safer, and would allow you to keep the J&J Smith from Springfield seperate from the J&J Smith from Shelbyville.