SQL Foreign key abrevation - sql

Are these T-SQL declarations equals?
CREATE TABLE Person
(
ID INT PRIMARY KEY,
NAME VARCHAR(60)
)
CREATE TABLE Dog
(
CHIP_ID INT PRIMARY KEY,
OWNER_ID INT REFERENCES Person(ID)
)
and
CREATE TABLE Person
(
ID INT PRIMARY KEY,
NAME VARCHAR(60)
)
CREATE TABLE Dog
(
CHIP_ID INT PRIMARY KEY,
OWNER_ID INT,
FOREIGN KEY(OWNER_ID) REFERENCES Person(ID)
)
I'm talking of course about the foreign key, I'm not sure if I have to specify it is a foreign key or not.
Thank you.

Yes, the DBMS see both as the same. But humans can many times miss important details when the code is cryptic. In fact, my preference is this:
CREATE TABLE Person(
ID INT not null,
Name VARCHAR(60) not null,
constraint PK_Person primary key( ID )
);
CREATE TABLE Dog(
ID INT not null,
OwnerID INT,
constraint PK_Dog primary key( CHIP_ID ),
constraint FK_Dog_Owner foreign key( OWNER_ID ) REFERENCES Person( ID )
);
Using the constraint clause not only defines the primary and foreign keys, but allow us to give them a meaningful name. And the surrogate key of each table should be named "ID". Any foreign keys in other tables will expand that name according to its context (RoleID). As you have in the Dog table with OwnerID. Another table with a FK to the same Person table may name it GroomerID or whatever else shows the role that person plays in the context of the table.
Also, as you can see, I prefer CamelCase with SQL Server, leaving OWNER_ID for Oracle.
Some even go so far as to place either NULL or NOT NULL after each column definition. But I find that adds clutter and doesn't really supply information even a beginning SQL developer doesn't already know. So I only supply NOT NULL when appropriate and let the default carry. Actually, in the later versions of Oracle and SQL Server, the NOT NULL for the primary key field is optional as the primary key is going to be defined as NOT NULL no matter what.
Long ago there seemed to be an informal contest to see who could cram the most operations into the fewest words or even characters. Just stay far away from that kind of thinking. But do make everything you write meaningful.
In general, use every opportunity to add meaningful information to the code. The computer doesn't care. Write to the other developers who will follow you.

Both T-SQL will create the foreign key you need. However, I believe the second approach where the code explicitely states "FOREIGN KEY..." is a good contribution to keep easy-maintenance and clean code for future software engineer understanding.

Related

SQL Server Different ways of creating table with foreign key

I need to create table with a foreign key. So far I have been doing that like this:
CREATE TABLE books
(
book_id NVARCHAR(15) NOT NULL UNIQUE,
author_id INT REFERENCES authors(author_id)
...
);
But my professor from university sent me exemplary scripts showing another way of dealing with foreign keys:
CREATE TABLE books
(
book_id NVARCHAR(15) NOT NULL UNIQUE,
author_id INT,
CONSTRAINT author_FK
FOREIGN KEY(author_id) REFERENCES authors(author_id)
...
);
Trying to find the difference between those, I made a research. Unfortunately I haven't found the answer, what I found was another way of creating table with foreign key (very similar to the second one):
CREATE TABLE books
(
book_id NVARCHAR(15) NOT NULL UNIQUE,
author_id INT,
FOREIGN KEY(author_id) REFERENCES authors(author_id)
...
);
Could you point out the differences between all of them?
Functionally, there is no difference between the two. The first is called an inline constraint (and can be used for check constraints as well).
There are two minor difference. The first is that the constraint keyword is not necessary of the inline reference, so inline references often do not name the constraint (constraint is allowed and you can name the reference, but that is not the syntax you show).
The second is that the foreign key reference can only use one column. For me, this is almost never an issue, because I almost always have synthetic primary keys rather than composite primary keys. However, the inline syntax is less powerful than the full constraint definition.
By the way, there is a third method which uses alter table. This is similar to your second method, but it allows for constraints to be added after a table has already been created.

What is the use of uc after constraint?

CREATE TABLE Persons
(
P_Id int NOT NULL,
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Address varchar(255),
City varchar(255),
CONSTRAINT **uc**_PersonID UNIQUE (P_Id,LastName)
)
It is absolutely good practice to name your constraints (otherwise SQL Server will name them with a random name, which makes it really difficult to upgrade more than one system with a general upgrade script).
It is good practice to use a prefix to see what type of constraint this is.
Common are
UQ, UC or UK for unique constraint / unique key
FK for foreign key
PK for the primary key
CK for a CHECK constraint
UPDATE
And it is good practice to add the table's name to the constraint (to avoid ambiguities). In your case this was:
CONSTRAINT uc_Persons_PersonID UNIQUE (P_Id,LastName)
Btw (Naming convention): It is quite common to use table names in singular form (Person instead of Persons). Read here: Table Naming Dilemma: Singular vs. Plural Names

How do I create a table whose rows reference 1 (and only 1) of 2 existing tables?

Here's my situation: I have two tables created with
CREATE DATABASE JsPracticeDb;
/* Create tables corresponding to the problems, solutions to
problems, and ratings of problems or solutions */
CREATE TABLE Problems (
id INT PRIMARY KEY NOT NULL,
prompt_code VARCHAR(3000),
test_func_code VARCHAR(3000),
test_input_code VARCHAR(3000)
);
CREATE TABLE Solutions (
id INT PRIMARY KEY NOT NULL,
problem_id INT,
solver_name VARCHAR(50),
code VARCHAR(3000),
FOREIGN KEY (problem_id) REFERENCES Problems(id) ON DELETE CASCADE,
);
and I was thinking about creating a table for rating Solutions, which I wrote as
CREATE TABLE Ratings (
id INT PRIMARY KEY NOT NULL,
solution_id INT,
stars TINYINT,
FOREIGN KEY (solution_id) REFERENCES Solutions(id) ON DELETE CASCADE
);
but then I realized I might actually want to have Problems rated as well. The "brute force" solution, as I see it, is
CREATE TABLE SolutionRatings (
id INT PRIMARY KEY NOT NULL,
solution_id INT,
stars TINYINT,
FOREIGN KEY (solution_id) REFERENCES Solutions(id) ON DELETE CASCADE
);
CREATE TABLE ProblemRatings (
id INT PRIMARY KEY NOT NULL,
problem_id INT,
stars TINYINT,
FOREIGN KEY (problem_id) REFERENCES Problems(id) ON DELETE CASCADE
);
but my programming intuition says there's a problem with the fact that I used copy-paste to write two sections of code that are almost identical. However, I can't think of any alternative solution that uses an intersection table or something like that also allows me to do a cascade delete. For example, I know I could do
CREATE TABLE RatedTables (
id TINYINT PRIMARY KEY NOT NULL,
table_name VARCHAR(9)
);
INSERT INTO RatedTables (table_name) VALUES ('Problems','Solutions');
CREATE TABLE Ratings (
id INT PRIMARY KEY NOT NULL,
rated_table_id TINYINT NOT NULL,
stars TINYINT,
FOREIGN KEY (rated_table_id) REFERENCES RatedTables(id)
);
but then how would I make it so that if a Solution with corresponding Ratings was deleted then those ratings would be too?????
You basically have two options but this is a good opportunity to go back and review your db structure.
The first option is to do something like this:
CREATE TABLE potential_link1 (
id int primary key,
...
);
CREATE TABLE potential_link2 (
id int primary key,
....
);
CREATE TABLE ratings (
id int primary key,
potential_link1 int references potential_link1(id) on delete cascade,
potential_link2 int references potential_link2(id) on delete cascade,
....
check(potential_link1 is null or potential_link2 is null),
check(potential_link2 is not null or potential_link1 is not null)
);
This works but as you can see it is a bit complex.
The second possibility is that since there are clear cases where a is dependent on the union of b and c then you may think about whether you can refactor your db structure to reflect that so you only need one table to link against.
There is nothing wrong with two tables looking so much alike. They contain different things and you won't want to select all three-star ratings no matter whether on problems or solutions for instance - you would always work with solution ratings or problem ratings.
But to have both ratings in one table is also not wrong and can be a good idea when you want ratings to behave the same, no matter whether on problem or solution (e.g. both shall have 1 to 5 stars, both can have a comment no longer then 200 chars, ...).
This could be done by simply giving the ratings table both a problem_id and a solution_id with foreign keys on the tables and fill always one or the other. With natural keys, the same would feel even more, well, natural:
problem(problem_no, data)
solution(problem_no, solution_no, data)
rating(problem_no, solution_no, data)
with rating.solution_no nullable and foreign keys on both parent tables.

Referential integrity over table portion

I'm designing a database and can't figure out how to model referential integrity.
I have the following tables
CREATE TABLE Groups
(
GroupId INT PRIMARY KEY,
GroupName VARCHAR(50)
)
CREATE TABLE GroupMembers
(
GroupId INT NOT NULL,
MemberId INT NOT NULL,
MemberName VARCHAR(50),
CONSTRAINT pk_GroupMember PRIMARY KEY (GroupId, MemberId)
)
CREATE TABLE Missions
(
MissionId INT PRIMARY KEY,
GroupId INT NOT NULL,
MissionName VARCHAR(50)
)
CREATE TABLE MissionRollAssignments
(
MissionId INT NOT NULL,
MemberId INT NOT NULL,
MemberRoll VARCHAR(50) --This will probably become RollId and move details to another table
)
Every mission will have assignments for some/all members of the corresponding group. There will be several missions associated with each group, but only one mission per group is active at a given time.
My question is:
Is it possible to enforce referenciay integrity for roll assignments such that only members
of the corresponding group (given by the MissionId) are selected? I know I can filter this from the GUI, but I'd feel more comfortable if I could create a FK constraint from MissionRollAssignments to GroupMembers while considering the GroupId indicated in the Mission.
A second question would be if you guys think this is a good way to model my domain, or maybe I should try a different approach.
Thanks in advance for any help on this.
Best regards,
Awer
You could put GroupId into MissionRollAssignments and then add two constraints as follows:
ALTER TABLE MissionRollAssignments
ADD CONSTRAINT fk1 FOREIGN KEY (GroupId, Memberid)
REFERENCES GroupMembers (GroupId, Memberid);
ALTER TABLE MissionRollAssignments
ADD CONSTRAINT fk2 FOREIGN KEY (GroupId, MissionId)
REFERENCES Missions (GroupId, MissionId);
To achieve this SQL Server first requires a (redundant) UNIQUE constraint on (GroupId, MissionId) in the Missions table. Other DBMSs are not so strict but SQL Server requires a FOREIGN KEY constraint to match exactly the columns of a uniqueness constraint.
You should use Foreign Keys to reinforce this, eg Mission.GroupId should refer to Group.GroupId.
Is it possible to enforce referenciay integrity for roll assignments such that only members of the corresponding group (given by the MissionId) are selected?
Yes. You need to use identifying relationships to propagate the GroupId all the way down to the bottom of this "diamond-shaped" dependency, similar to this:
Note FK1 and FK2 in front of MissionRollAssignment.GroupId, indicating that foreign keys exist up the both "sides" of this "diamond-shaped" dependency.
As single active mission can be modeled as a foreign key in the opposite direction, in this case as Group {GroupId, ActiveMissionNo} that references the Mission primary key.
Such circular foreign key presents a "chicken-and-egg" problem on a DBMS that doesn't support deferred constraints (which SQL Server doesn't). However, you can just leave Group.ActiveMissionNo NULL-able, so a DBMS that enforces foreign keys in a MATCH SIMPLE fashion (which SQL Server does) will ignore the whole composite foreign key if just one of its fields is NULL. This will allow you to temporarily "disable" the foreign key and break the "chicken-and-egg" cycle when inserting new data.

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