sql server relationships between 3 tables - sql

I have these tables I want to create for a Grooming Shop:
CREATE TABLE Pets(
Id int NOT NULL PRIMARY KEY IDENTITY
,Name varchar(20)
,Breed varchar(35)
,[Weight] decimal (10,2) NOT NULL
,Cat bit NOT NULL
,Dog bit NOT NULL
)
CREATE TABLE UserInfo(
Id int NOT NULL PRIMARY KEY IDENTITY
,FirstName varchar(15) NOT NULL
,LastName varchar(30) NOT NULL
,PhoneNumber varchar(10) NOT NULL
,EmailAddress varchar(30) NOT NULL
,AddressId int NOT NULL FOREIGN KEY REFERENCES [Address](Id)--Address Table already created
,PetId int NOT NULL FOREIGN KEY REFERENCES Pets(Id)
)
CREATE TABLE Appointments(
Id int NOT NULL PRIMARY KEY IDENTITY
,[Date] date NOT NULL
,UserInfoId int NOT NULL FOREIGN KEY REFERENCES UserInfo(Id)
,PetId int NOT NULL FOREIGN KEY REFERENCES Pets(Id)--?
)
My Appointments table will have the UserInfo but should it also have the Pets info? If there can be more than 1 pet for each appointment, and more than on appointment for each pet... When I wanted the UserInfo to point to the Pets table, because each user will have at least one pet.

No, you needn't have pets info in appointment table because the information related to pet can be retrieved from the UserInfo table itself.

Since each user will have one or more pets and a pet presumably has only one owner, I suggest you add UserInfoId to the Pets table and remove PetId from the UserInfo table. I also suggest you remove PetId from the Appointment table and create a separate table for the pets included in the appointment. So the appointment is actually with the owner, who may bring one or more pets.
CREATE TABLE UserInfo(
Id int NOT NULL PRIMARY KEY IDENTITY
,FirstName varchar(15) NOT NULL
,LastName varchar(30) NOT NULL
,PhoneNumber varchar(10) NOT NULL
,EmailAddress varchar(30) NOT NULL
,AddressId int NOT NULL FOREIGN KEY REFERENCES [Address](Id)
);
CREATE TABLE Pets(
Id int NOT NULL PRIMARY KEY IDENTITY
,UserInfoId int NOT NULL FOREIGN KEY REFERENCES UserInfo(Id)
,Name varchar(20)
,Breed varchar(35)
,[Weight] decimal (10,2) NOT NULL
,Cat bit NOT NULL
,Dog bit NOT NULL
);
CREATE TABLE Appointments(
Id int NOT NULL PRIMARY KEY IDENTITY
,[Date] date NOT NULL
,UserInfoId int NOT NULL FOREIGN KEY REFERENCES UserInfo(Id)
);
CREATE TABLE AppointmentPets(
AppointmentId int NOT NULL
,PetId int NOT NULL
CONSTRAINT PK_AppointmentPets PRIMARY KEY(AppointmentId, PetId)
);

You can have petID in appoinment table but it's not needed since you can link appoinment -> userinfo using UserInfoId relationship and then you can link userinfo -> pets using petid relationship.
This scenario is very well known as Transitive relationship. I.e, If A -> B and B -> C exist then A -> C can be formed.

You should know how you intend to use these tables, that is, what are your business rules. Then, you will have a better design for your table. If you are not sure, then you might have to redesign your tables again.
Here are some points to think about:
You don't want to repeat user info for each pet they have, right ? (Normalization)
You can map users to pets in a separate table or in appointments table. Its possible that you have a many to many relationship between users and pets, that is multiple users have one pet and multiple pets are owned by a user. You could link users to pets in a separate table.
You could split user info into two tables - userinfo and useraddress table and link them with user id, since family in the same home can share a pet. (Normalization)
Replace cat bit, dog bit columns by animal column. (Minor suggestion)

Related

SQL INSERT INTO with empty foreign table

I have a table with 3 values and a foreign table.
Tbl_Person
ID int PRIMARY KEY IDENTITY(1,1)
CarID int REFERENCES Tbl_Car(ID)
Name nvarchar(20)
Tbl_Car
ID int PRIMARY KEY IDENTITY(1,1)
Color nvarchar(30)
I now want to create a new Person with a new Car without caring about the Car table, sort of like this:
INSERT INTO Tbl_Person (Car, Name)
VALUES ('dont know what goes here', 'Timmy')
I'm not sure what to put inside the Car column since I just want it to exist but not care about its values just yet.
I'm sure its quite easy to find online but I have no idea how to Google for this particular problem.
You could alter the logical model to denormalize 'CarID' (remove it from 'Persons' table) and create a new table to store persons' cars called 'Person_Cars'. This avoids inserting NULL values into the model when the car is unknown. Depending on which table constraints are applied it also could allow for persons to have more than 1 car. Also, prefixing tables with 'tbl_' is not necessary. Also, both Persons and Cars seem likely to benefit from a unique constraint on the NVARCHAR column(s).
Persons
ID int not null PRIMARY KEY IDENTITY(1,1)
Name nvarchar(20) unique not null
Cars
ID int not null PRIMARY KEY IDENTITY(1,1)
Car nvarchar(30) unique not null
Person_Cars
ID int not null PRIMARY KEY IDENTITY(1,1)
PersonID int not null REFERENCES Persons(ID)
CarID int not null REFERENCES Cars(ID)
You can use NULL:
CREATE TABLE Tbl_Car (
ID int PRIMARY KEY IDENTITY(1,1),
Color nvarchar(30));
CREATE TABLE Tbl_Person (
ID int PRIMARY KEY IDENTITY(1,1),
CarID int REFERENCES Tbl_Car(ID),
Name nvarchar(20));
INSERT INTO Tbl_Person (CarID, Name) VALUES (NULL, 'Timmy');
DBFIDDLE
You have to do a logical operation. You can not just give the car an ID but it must have a name.
change Tbl_Car to :
CREATE TABLE Tbl_Car (
ID int PRIMARY KEY IDENTITY(1,1),
CarName nvarchar(30),
Color nvarchar(30));
To enter the Tbl_Person table, you must know the name of the person's car :
INSERT INTO Tbl_Person VALUES(CarID,Name)
SELECT ID,'Timmy'
FROM Tbl_Car
WHERE CarName = 'BMW'

How to index a SQL table on a column in another table

I have the schema below - Let's pretend that there are 2 countries, A and B.
Country A has 1000 teams whereas country B has 100,000,000 - If I want to quickly query results based off which country the team is in, how would I construct my index?
Teams cannot change country if that helps.
Indexing a table depend upon knowing real schema.
For this simple table schema, I will create only Trusted FK between tables, at least this will be my first try.
Assuming Countryid,Teamid,Resultid are auto increment.
CREATE TABLE Country
(
id INT IDENTITY(1, 1) PRIMARY KEY,
CountryName VARCHAR(100) NOT NULL
);
CREATE TABLE Team
(
id INT IDENTITY(1, 1) PRIMARY KEY,
TeamName VARCHAR(100) NOT NULL,
CountryID INT NOT NULL
);
ALTER TABLE dbo.Team WITH CHECK
ADD CONSTRAINT FK_Team_CountryID
FOREIGN KEY(CountryID) REFERENCES dbo.Country(id);
ALTER TABLE dbo.Team WITH CHECK
CHECK CONSTRAINT FK_Team_CountryID;
--Just verify that newly created FK is trusted or not.
SELECT
name,
is_disabled,
is_not_trusted
FROM
sys.foreign_keys
WHERE
name = 'FK_Team_CountryID';
CREATE TABLE Result
(
id INT IDENTITY(1, 1) PRIMARY KEY,
TeamId INT NOT NULL,
Result INT NOT NULL
);
-- I have no idea how you are storing Result,so ignore it
ALTER TABLE dbo.Result WITH CHECK
ADD CONSTRAINT FK_Result_TeamId
FOREIGN KEY(TeamId) REFERENCES dbo.Team(id);
ALTER TABLE dbo.Result WITH CHECK
CHECK CONSTRAINT FK_Result_TeamId;
May be after seeing query plan of real query, I will De-normalise Result table to add Countryid , but for now it is not require since country table will be small

SQL check constraint

I'm trying to create a constraint to check that a project can have only one employee whose role is project leader but at the same time can have other employees with different roles.
My table definition:
CREATE TABLE employee
( employee_id INT NOT NULL PRIMARY KEY
,employee_role VARCHAR(15) NOT NULL
, CHECK (employee_role in ('project_leader', 'administrator', 'member'))
)
CREATE TABLE project
( project_id INT NOT NULL PRIMARY KEY
, name VARCHAR(50)
, employee_id INT NOT NULL
, employee_role VARCHAR(15) NOT NULL
, CONSTRAINT employee_project_FK
FOREIGN KEY (employee_id, employee_role)
REFERENCES employee (employee_id, employee_role)
, CONSTRAINT only_one_project_leader
CHECK (employee_role = 'project_leader')
) ;
It's unclear to me how this can be expressed in a constraint and what I need to change. If anyone would inform me what I'm doing wrong, I'd appreciate it.
You are missing a table. Your data structure wants three tables:
Employee, which lists information about employees
Project, which lists information about projects
ProjectEmployee, which is an association table between the two
If you want a constraint that a project has only one leader, then you can simply add a column to Project called ProjectLeader. This will enforce the constraint, because there is only one slot per project for the leader. If you have to have a leader, then add in a check constraint to be sure this is not NULL.
A sign that something is wrong with the data model is that project_id is a primary key in the project table. This implies that for a given project_id, there is only one employee. I don't think that is what you intend.
EDIT:
The tables would look something like:
CREATE TABLE project
( project_id INT NOT NULL PRIMARY KEY,
name VARCHAR(50),
project_leader int references employee(employee_id)
) ;
CREATE TABLE projectemplyee
( projectemployee_id INT NOT NULL PRIMARY KEY,
project_id int references project(project_id),
employee_id int references employee(employee_id),
employee_role VARCHAR(15) NOT NULL
) ;
There is only one slot for a leader in each project. You do not need a constraint to enforce the one-ness.

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.

SQL Unknown number of attributes

I have a one-to-many relationship between two classes for this situation. I have a swimming competition and that competition can have x swimmers.
How can I create an SQL table for this, I know I will have to use the Primary Key of Swimmers as a foreign key in the Swimming competition but I have no idea how to represent the correct number of attributes since it's unknown.
This is called a m:n relationship and usually solved with a mapping table.
Something like this:
create table swimmer
(
id integer not null primary key,
lastname varchar(100) not null,
firstname varchar(100)
)
create table competition
(
id integer not null primary key,
name varchar(50) not null,
comp_date date not null
)
create table participant
(
swimmer_id integer not null,
competition_id integer not null,
rank_in_competetion integer,
primary key (swimmer_id, competition_id),
constraint fk_participant_swimmer (swimmer_id) references swimmer(id),
constraint fk_participant_competition (competition_id) references competition(id)
)