I have got two tables in SQL Server 2005:
USER Table: information about user and so on.
COUNTRY Table : Holds list of whole countries on the world.
USER_COUNTRY Table: Which matches, which user has visited which county.
It holds, UserID and CountryID.
For example, USER_COUNTRY table looks like this:
+----+--------+-----------+
| ID | UserID | CountryID |
+----+--------+-----------+
| 1 | 1 | 34 |
| 2 | 1 | 5 |
| 3 | 2 | 17 |
| 4 | 2 | 12 |
| 5 | 2 | 21 |
| 6 | 3 | 19 |
+----+--------+-----------+
My question is that: When a user is deleted in USER table, how can I make associated records in USER_COUNTRY table deleted directly. Maybe, by using Foreign Key Constaint?
You have to define a foreign key in USER_COUNTRY that points to USER.UserID and set cascaded deletion:
CREATE TABLE USER_COUNTRY (
...
CONSTRAINT USER_COUNTRY_FK1 FOREIGN KEY (UserID)
REFERENCES USER(UserID)
ON DELETE CASCADE
);
Yes, you could set your foreign key relationship Delete rule to Cascade.
I guess CASCADE is your only option. But do you really want to hard delete records like this? Context: I'm a data fiend.
Related
Let's say I have a Persons and Books table that were associated.
PERSONS TABLE BOOKS TABLE
uid | userCode | name id| name | owner
------------------------- --------------------------
1 | 0xc! | john 1 | book foo | 0xc!
2 | li5$ | doe 2 | foo book | li5$
3 | 1y&t | temp 3 | ddia | 0xc!
Currently persons.usercode serves as the primary key and hence the foreign key on associated tables. I would like to change the primary key of the persons table to persons.uid. So now I want the books table to look like
PERSONS TABLE BOOKS TABLE
uid | usercode | name id| name | owner
------------------------- --------------------------
1 | 0xc! | john 1 | book foo | 1
2 | li5$ | doe 2 | foo book | 2
3 | 1y&t | temp 3 | ddia | 1
Dropping and adding the new primary key constraint shouldn't be a problem. However, how do I go about updating the entire books.owner column with the new primary key if I have over 10,000 rows in the books table
You need to drop/disable the current foreign key & re-add it. You may also need to find out the name of that primary/foreign key constraint before dropping.
ALTER TABLE "PERSONS"
DROP CONSTRAINT "primary_fkey"
UPDATE BOOKS bk SET owner=(SELECT uid FROM PERSONS WHERE userCode = bk.owner);
ALTER TABLE "PERSONS"
ADD CONSTRAINT "primary_fkey"
FOREIGN KEY ("uid")
REFERENCES BOOKS("owner")
ON UPDATE CASCADE;
I have the following supertype/multiple subtypes tables in SQL Server
supertype: Doctor and subtypes: Paediatrician, Orthopedic and Dentist
create table Doctor
(
DoctorID int primary key,
Name varchar(100),
-- add some other common attributes (all of vendor, sponsor, volunteer have) here.
)
create table Paediatrician
(
PaediatricianId int primary key,
DoctorID int foreign key references Doctor(DoctorID)
-- add some other attributes related to Paediatrician here.
)
create table Orthopedic
(
OrthopedicId int primary key,
DoctorID int foreign key references Doctor(DoctorID)
-- add some other attributes related to Orthopedic here.
)
create table Dentist
(
DentistId int primary key,
DoctorID int foreign key references Doctor(DoctorID)
-- add some other attributes related to Dentisthere.
)
My business logic is that a doctor can be either a Paediatrician, Dentist or an Orthopedic. Cannot be more than one of the subtypes. Based on the above design this is not enforced. I can create Doctor with Id = 1 and then go to Dentist and Orthopedictables and assign DoctorId value of 1 in both tables. How do I enforce it so that a doctor can be present at only one table?
I would arrange this bit differently. I would have 3 tables, a Doctor table (like you already have), a Specialist table and a SpecialistAttributes table.
The Doctor table contains all the Doctors' info, easy.
The Specialist Table contains your SpecialistTypeID and SpecialistDescription etc.
Your 3 example specialists would each be a row in this table.
The SpecialistAttributes table contains all the attributes needed for the specialists. In your Doctor table, you have a foreign key to lookup the SpecialistTypeID, so there can be only 1, then the SpecialistType has a number of SpecislaistAttibutes it can link to.
The other benefit of organising your data this way is that of you need to add any specialists roles or attributes, you don't need to change the structure of your database, just add more rows.
Doctor Table
| ID | Name | Specialist_FK |
---------------------------------
| 1 | Smith | 2 |
| 2 | Davies | 3 |
| 3 | Jones | 3 |
Specialist Table
| ID | Speciality |
----------------------
| 1 | Paediatrician |
| 2 | Orthopedic |
| 3 | Dentist |
SpecialistAttribute Table
| ID | SpecialityID+FK | Description | Other |
------------------------------------------------------------
| 1 | 1 | Paediatrician Info 1 | Other Info |
| 2 | 1 | Paediatrician Info 2 | Other Info |
| 3 | 2 | Orthopedic Info 1 | Other Info |
| 4 | 2 | Orthopedic Info 1 | Other Info |
| 5 | 3 | Dentist Info 1 | Other Info |
| 6 | 4 | Dentist Info 1 | Other Info |
There is no inbuild constraints/feature in the SQL server to handle this. You need to write custom login for it. Either in the procedure or Trigger.
You can write a stored procedure which would be responsible to insert in these tables. before insert, it will validate that if doctor id already exists in any of the tables if yes then an error will be custom raised otherwise procedure will insert the record in the respective table.
I have two tables: Talks and Days. Talks looks something like:
+----+----------------------------------+--------+
| Id | Name | Leader |
+----+----------------------------------+--------+
| 1 | How to improve revenue for tacos | Tacob |
| 2 | Improving sales potential | Bocat |
+----+----------------------------------+--------+
and the Days:
+--------+-----+
| TalkId | Day |
+--------+-----+
| 1 | Mon |
| 1 | Tue |
| 1 | Thu |
| 2 | Mon |
| 2 | Tue |
+--------+-----+
TalkId is a foreign key referencing the Talks table.
The foreign key enforces the relationship of "A Day requires a Talk". However, I would like to also enforce the reverse relationship "A Talk requires at least a Day".
I know that this constraint is similar to a Many-to-many relationship, where both records depend on each other. However, in this case, many days reference a talk but only one talk references many days.
Another problem is that after creating such a constraint, how would one insert both records at once?
I have searched for other questions and only found cases of Many-to-many relationships which will turn out like so:
+----+----------------------------------+--------+
| Id | Name | Leader |
+----+----------------------------------+--------+
| 1 | How to improve revenue for tacos | Tacob |
| 2 | Improving sales potential | Bocat |
+----+----------------------------------+--------+
+----+-----+
| Id | Day |
+----+-----+
| 1 | Mon |
| 2 | Tue |
| 3 | Thu |
| 4 | Mon |
| 5 | Tue |
+----+-----+
+--------+-------+
| TalkId | DayId |
+--------+-------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 4 |
| 2 | 5 |
+--------+-------+
Where TalkId references Talks's Id and DayId references Days's Id.
Edit:
Ignore what I requested for above.
What I hope to be able to do:
SELECT all valid Talks
SELECT all valid Days
What I hope to be unable to do:
INSERT a Talk without a Day
INSERT a Day without a Talk
It sounds like you want a simple foreign key relationship:
alter table days add constraint fk_days_talkid foreign key (talkid) references talks(talkid);
This guarantees that talkid is valid. Then you declare days.talkid to be not null and you are guaranteeing the relationship you describe.
-- Day named (TheDay) exists.
--
Calendar {TheDay}
PK {TheDay}
-- Talk (TalkID) titled (TalkName), presented by (Leader) is by default
-- scheduled on (DefaultDay).
Talk {TalkID, TalkName, Leader, DefaultDay}
PK {TalkID}
AK {TalkName}
FOREIGN KEY {DefaultDay} REFERENCES Calendar {TheDay}
--Talk (TalkID) is also scheduled on (TheDay).
--
TalkDay {TalkID, TheDay}
PK {TalkID, TheDay}
FOREIGN KEY {TalkID} REFERENCES Talk {TalkID}
FOREIGN KEY {TheDay} REFERENCES Calendar {TheDay}
Note PK = primary key
AK = alternate key (unique)
All attributes (columns) NOT NULL
Select all days for a specific talk:
select TalkName, DefaultDay as TalkDay
from Talk
where TalkName = 'How to improve revenue for tacos'
union
select TalkName, b.TheDay as TalkDay
from Talk as a
join TalkDay as b on b.TalkID = a.TalkID
where a.TalkName = 'How to improve revenue for tacos'
Select all talks on a specific day:
select TalkName, DefaultDay as TalkDay
from Talk
where DefaultDay = 'Tue'
union
select TalkName, b.TheDay as TalkDay
from Talk as a
join TalkDay as b on b.TalkID = a.TalkID
where b.TheDay = 'Tue'
I got 2 tables User and Company.
These two have a inventory.
DataTables
Item
ItemId | Name
================
1 | Glass
4 | Wood
User
UId | Name
============
1 | Max
Company
CId | Name
==================
1 | EvilCorp
Inventory
RowId | UId | CId | ItemId | amount
=================================================
1 | 2 | Null | 4 | 10
2 | 23 | Null | 4 | 5
3 | Null | 1 | 1 | 7
4 | Null | 1 | 4 | 70
Let say I have 500 users and 300 companys and every one has 20 inventory slots, I will have 16000 null values in my Inventory table (6000 UId nulls + 10000 CId nulls).
I want a SQL query that will say this information.
Result
Owner | Item | Amount
===========================
MAX | Wood | 10
EvilCorp | Glass | 7
EvilCorp | Wood | 40
My problem is that my Inventory table is bad due to all the nulls that will appears against CId when the record is for a User, and vice versa.
Do you know how to create a good table, without huge or/and complex SQL queries?
You have Users, Companies and Owners. Users and Companies are Owners.
This is a common situation that can be put into tables various ways.
Every table holds the rows that make some statement true:
// "user [OId] has ..."
User(OId,...)
fk OId references Owner -- a user is an owner
// "company [OId] has ..."
Company(OId,....)
fk OId references Owner -- a company is an owner
// "Owner [OId] has ..."
Owner(OId,...)
// "[OId] owns [n] of item [itemId]"
Inventory(Oid,ItemId,amount)
fk OId references Owner
fk ItemId references Item
This comes from choosing tables that make plain statements about parts of your application. For variations read about subtables and supertables for subtypes and supertypes. See this answer or this one.
If I have a User table and a Roles table.
What is the usual practice/pattern for adding the relationship?
Do I create an extra column in the User table for the RoleID or do people usually create a Relationships table like so:
Relationships Table
RelationshipID | UserID | RoleID |... any other relations a user might have
for the last bit, as a user you might create an endless amount of different types of things that all need to be related to you... do you instead add the relationship to each individual table created for each individual thing.. for example:
Pages Table
PageID | Title | Content | Author (UserID)
and so another table would also be similar to this:
Comments Table
CommentID | Comment | Author (UserID)
In this case, I would need to expand upon the Relationships table if I were to do it that way:
Relationships Table
RelationshipID | UserID | RoleID | CommentID
and i'd probably only want to fill in the UserID and CommentID as this relationship is not for the Roles... that is governed by another entry. so for example the values might be put in for a comment relationship:
AUTO | 2 | NULL | 16
I could imagine a multi purpose Revisions table being handy...
Revisions Table
RevisionID | DateCreated | UserID | ActionTypeID | ModelTypeID | Status | RelatedItemID
---------------------------------------------------------------------------------------
1 | <Now> | 3 | 4 (Delete) | 6 (Page) | TRUE | 38
2 | <Now> | 3 | 1 (Delete) | 5 (Comment) | TRUE | 10
3 | <Now> | 3 | 1 (Add) | 5 (Comment) | FALSE | 10
but not for a general Relationships table...
Does this sound correct?
Edit since comments:
They stated that the relationships table should be made due to the many-to-many (data model)
So let's take my previous example of my possible relationship table:
Relationships Table Old
RelationshipID | UserID | RoleID | CommentID... etc
Should it actually be something more like this:
Relationships Table New
RelationshipID | ItemID | LinkID | ItemType | LinkType | Status
---------------------------------------------------------------------------------
1 | 23(PageID) | 7(UserID) | ("Page") | ("User") | TRUE
2 | 22(CommentID) | 7(UserID) | ("Comment") | ("User") | TRUE
3 | 22(CommentID) | 23(PageID) | ("Comment") | ("Page") | TRUE