SQL Foreign key issue with 2 parent tables - sql

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.

Related

Add unique constraint on fields from different tables

I have two tables/entities:
One table Users with these 3 fields :
id | login | external_id
There is a unique constraint on external_id but not on login
And another table User_Platforms that have these 3 fields :
id | user_id | platform_name
There is a #OneToMany relation between Users and Platforms. ( One user can have multiple platforms).
Is there a way to put a unique constraint on the fields login ( from Users table) and platform_name ( from User_Platforms table) to avoid having multiple users with the same login on the same platform ?
I was thinking of duplicating login field inside User_Platforms table to be able to do it easily. Is there a better way ?
UNIQUE constraints cannot span multiple tables. In the model you are presenting "as is" it's not possible to create a unique constraint that will ensure data quality.
However, if you are willing to add redundancy to the data model you can enforce the rule. You'll need to add the column login as a redundant copy in the second table. This will change the way you insert data in the second table, but will ensure data quality.
For example:
create table users (
id int primary key not null,
login varchar(10) not null,
external_id varchar(10) not null,
constraint uq1 unique (id, login)
-- extra UNIQUE constraint for redundancy purposes
);
create table user_platforms (
id int primary key not null,
user_id varchar(10) not null,
platform_name varchar(10) not null,
login varchar(10) not null, -- new redundant column
constraint fk1 foreign key (id, login) references users (id, login),
-- FK ensures that the redundancy doesn't become stale
constraint uq2 unique (platform_name, login) -- finally, here's the prize!
);

Create 2 tables having same column but not a primary key

( Question is only for a college project as I'm stuck with the requirement)
I want to create 2 tables in SQL Server, say 'table1' and 'table2' in the same database. Both should have a column say 'col1' which is not a primary key.
So how should I create it so that when I insert data into one table, the other gets updated automatically?
NOTE: So this is for a college project, we are asked to make a specific type of primary keys so referencing that is not an option, now I have to have same entity in 2 different tables, so is a good idea to somehow reference them, any ideas?
For eg, en employee's project details will have his/her empID and the dependants' table will have empID as well. But I cannot make it primary key since that is already defined by the Professor. But updating one should update another as well, does that make sense?
You can have two types of parent-child relationship.
Identifying relationship : Here, child depends on the parent to identify itself. E.g. Project requires Employee to exist. Here, Project should have EmployeeId part of its primary key or EmployeeId as its primary key. If EmployeeId is primary key of project then, an employee can have only one project.
CREATE TABLE Employee
(
EmployeeId INT,
EmployeeName VARCHAR(255) NOT NULL,
PRIMARY KEY(EmployeeId)
)
GO
CREATE TABLE EmployeeProject
(
EmployeeId INT,
EmployeeName VARCHAR(255) NOT NULL,
PRIMARY KEY(EmployeeId),
FOREIGN KEY (EmployeeId) REFERENCES Employee(EmployeeId),
)
GO
Non-identifying relationship: Here, child does not depend on the parent to identify itself. E.g. Project can be defined without Employee. Here, Project can have EmployeeId as foreign key. If EmployeeId is NOT NULL column, then it is mandatory to have an employee. If EmployeeId is NULL column, then it is not mandatory to have an employee.
CREATE TABLE Employee
(
EmployeeId INT,
EmployeeName VARCHAR(255) NOT NULL,
PRIMARY KEY(EmployeeId)
)
GO
CREATE TABLE EmployeeProject
(
EmployeeProjectId INT,
EmployeeName VARCHAR(255) NOT NULL,
EmployeeId INT NOT NULL, -- Can be NULL, if it is not mandatory
PRIMARY KEY(EmployeeProjectId),
FOREIGN KEY (EmployeeId) REFERENCES Employee(EmployeeId),
)
GO
First while creating tables, the entity which is to be refereced as foreign key, make it unique:
Table1:
[SIN] int NOT NULL UNIQUE,
Second, in another table where calling [SIN] as FK, put conditions for update and delete:
Table2:
[SIN] INT CONSTRAINT [SIN_FK1] FOREIGN KEY REFERENCES Employee([SIN]) ON DELETE SET NULL ON UPDATE CASCADE
By doing this, whenever you update or delete records in Table1, the corresponding record in Table2 will be updated.

Create three tables with the same auto increment primary key and name

Cannot find a good answer for this online. I need to create three tables as an example below, a parent with an auto increment ID that will then link to the two child tables (Subject and Comment) with the same exact ID and cascade back if that parent ID is deleted.
Any ideas on how to solve?
I have googled and am extremely confused as to how to solve this one. I have a decent amount of experience with SQL, but not with creating tables and relationships.
CREATE TABLE Parent
(
ParentID INT NOT NULL IDENTITY PRIMARY KEY,
Email VARCHAR(50) NOT NULL,...
)
CREATE TABLE Subject
(
ParentID INT NOT NULL PRIMARY KEY,
Subject
)
CREATE TABLE Comment
(
ParentID INT NOT NULL PRIMARY KEY,
Comment VARCHAR(100)
)
Use a 1 to 1 relationship with on delete cascade:
CREATE TABLE Parent(
ParentID INT NOT NULL IDENTITY PRIMARY KEY,
Email VARCHAR(50) NOT NULL,...
)
CREATE TABLE Subject(
ParentID INT NOT NULL PRIMARY KEY,
Subject,
CONSTRAINT fk_SubjectParentId FOREIGN KEY (ParentID)
REFERENCES Parent (ParentID) ON DELETE CASCADE
)
CREATE TABLE Comment(
ParentID INT NOT NULL PRIMARY KEY,
Comment VARCHAR(100),
CONSTRAINT fk_CommentParentId FOREIGN KEY (ParentID)
REFERENCES Parent (ParentID) ON DELETE CASCADE
)
This is known as a 1 to 1 relationship, since both ends of the foreign key are unique within their table.
Though I have to agree with Mitch Wheat comment, cascading deletes is something to use with caution. by specifying cascade delete, you are telling the database engine to delete the related records whenever a parent record is deleted. Not having that cascade delete option will simply throw an error if you attempt to delete a record that is referenced by another table. This forces you, as a developer, to think about the side effects of deleting rows from the parent table and basically acts as a "Are you sure you want to delete?" guard against unwanted deletes.

Master child table with foreign key and primary key

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.

Foreign key on same table

I have a db schema that includes
Group{name, group.id, parent.id} with key {group.id}
In this schema all parent.id's must either already exist in the group.id column or be null.
How can i translate this constraint into SQL while creating the table?
Thanks
A regular foreign key should suffice. It won't perform any checks if the field is null. The precise syntax may depend slightly on the SQL dialect, but it would look something like
create table Group_ (
name varchar(30) not null,
groupid int not null primary key,
parentid int null foreign key references Group_ (groupid) )