Identity column separate from composite primary key - sql

I have a table representing soccer matches:
Date
Opponent
I feel {Date,Opponent} is the primary key because in this table there can never be more than one opponent per date. The problem is that when I create foreign key constraints in other tables, I have to include both Date and Opponent columns in the other tables:
Soccer game statistics table:
Date
Opponent
Event (Goal scored, yellow card etc)
Ideally I would like to have:
Soccer matches table:
ID
Date
Opponent
Soccer match statistics table:
SoccerMatchID
Event (Goal scored, yellow card etc)
where SoccerMatch.ID is a unique ID (but not the primary key) and {Date,Opponent} is still the primary key.
The problem is SQL Server doesn't seem to let me define ID as being a unique identity whilst {Date,Component} is the primary key. When I go to the properties for ID, the part signalling unique identifying is grayed-out with "No".
(I assume everyone agrees I should try to achieve the above as it's a better design?)

I think most people don't use the graphical designer to do this, as it's the graphical designer that's preventing it, not SQL Server. Try running DDL in a query window:
ALTER TABLE dbo.YourTable ADD ID INT IDENTITY(1,1);
GO
CREATE UNIQUE INDEX yt_id ON dbo.YourTable(ID);
GO
Now you can reference this column in other tables no problem:
CREATE TABLE dbo.SomeOtherTable
(
MatchID INT FOREIGN KEY REFERENCES dbo.YourTable(ID)
);
That said, I find the column name ID completely useless. If it's a MatchID, why not call it MatchID everywhere it appears in the schema? Yes it's redundant in the PK table but IMHO consistency throughout the model is more important.
For that matter, why is your table called SoccerMatch? Do you have other kinds of matches? I would think it would be Matches with a unique ID = MatchID. That way if you later have different types of matches you don't have to create a new table for each sport - just add a type column of some sort. If you only ever have soccer, then SoccerMatch is kind of redundant, no?
Also I would suggest that the key and unique index be the other way around. If you're not planning to use the multi-column key for external reference then it is more intuitive, at least to me, to make the PK the thing you do reference in other tables. So I would say:
CREATE TABLE dbo.Matches
(
MatchID INT IDENTITY(1,1),
EventDate DATE, -- Date is also a terrible name and it's reserved
Opponent <? data type ?> / FK reference?
);
ALTER TABLE dbo.Matches ADD CONSTRAINT PK_Matches
PRIMARY KEY (MatchID);
ALTER TABLE dbo.Matches ADD CONSTRAINT UQ_Date_Opponent
UNIQUE (EventDate, Opponent);

Related

Designing the primary key in associative table

Suppose I have an artist table like:
id
name
1
John Coltrane
2
Springsteen
and a song table like:
id
title
1
Singing in the rain
2
Mimosa
Now an artist can write more than one song, and a song can be written by more than one artist. We have a many-to-many relation. We need an associative table!
How to design the primary key of the associative table?
One way would be to define a composite key of the two foreign keys, like this:
CREATE TABLE artist_song_map(
artist_id INTEGER,
song_id INTEGER,
PRIMARY KEY(artist_id, song_id),
FOREIGN KEY(artist_id) REFERENCES artist(id),
FOREIGN KEY(song_id) REFERENCES song(id)
)
Another way would be to have a synthetic primary key, and impose an unique constraint on the tuple of the two foreign keys:
CREATE TABLE artist_song_map(
id INTEGER PRIMARY KEY AUTOINCREMENT,
artist_id INTEGER,
song_id INTEGER,
UNIQUE(artist_id, song_id),
FOREIGN KEY(artist_id) REFERENCES artist(id),
FOREIGN KEY(song_id) REFERENCES song(id)
)
Which design choice is better?
Unless you define the table as WITHOUT ROWID both queries will create the same table.
The column id in your 2nd way adds nothing but an alias for the column rowid that will be created in any of the 2 ways.
Since this is a bridge table, you only need to define the combination of the columns artist_id and song_id as UNIQUE.
If you want to extend your design with other tables, like a playlist table, you will have to decide how it will be linked to the existing tables:
If there is no id column in artist_song_map then you will link
playlist to song and artist, just like you did with
artist_song_map.
If there is an id column in artist_song_map then you can link playlist directly to that id.
I suggest that you base your decision not only on these 3 tables (song, artist and artist_song_map), but also on the tables that you plan to add.
Logically the both design is the same. But from administration aspect the identity design is more efficient. Less disk fragmentation and future redesign or maintenance will be easier.
Bridge tables normally don't require a ID(auto_inCREMNT) to identify the rows.
The linking columns(foreign key) are the main point, as thea link artists to a 8or songs)
only when you need special attributes to that bridge or you want to reference a row of that bridge table and don't want to have ttwo linking columns, then you would use such an ID field, but as i said normally you never need it
While, generally, the differences are minor, the composite/compound foreign key design sounds more natural. A separate primary key together with the associated index take additional space in the database. Further, if you use a composite primary key, you can declare the table as WITHOUT ROWID. According to the official docs, "in some cases, a WITHOUT ROWID table can use about half the amount of disk space and can operate nearly twice as fast".

What does it mean when there is no "Id" at the end of a column name which appears to be a foreign key?

This is the databases ERD my final project for school is on (at the bottom), I am required to make a database using this information. I understand how to add the tables that are setup like 'trainer' and even how to add self-joining tables to my database, but something we have NOT learned is what it means or what to do when there is no Id at the end? Like 'evolvesfrom' and 'pokemonfightexppoint'.
Do you not have to add an Id at the end? From what my teacher taught us, I assumed you did. From what I see in this ERD is how evolvesfrom is self-joining itself to pokemonId. I know how to complete this only when there is an Id at the end of evolvesfrom.
For something like trainerId, it is super easy to understand how to add the constraints and everything like so:
CREATE TABLE trainer (
trainerId INT IDENTITY(1, 1),
trainerName VARCHAR(50) NOT NULL,
CONSTRAINT pk_trainer_trainerId PRIMARY KEY (trainerId)
);
I just don't understand how to do this when there is no Id added. For the pokemonFight table, it is noted that "It is assumed that a
Pokémon can play any battles at any battle locations. In other words, the battle experience points are functionally dependent on Pokémon, battle, and battle location", if that makes a difference.
If possible, could anyone show me an example on how to add a table, with constraints on either the pokemon or the pokemonFight table? (obviously you don't have to include the data types or anything).
Thank you in advance.
I am using SQL Server.
There is no required naming convention for columns in SQL Server that differentiates between a data column, a primary key column or a foreign key column.
The only constraints on column names are that they follow the rules for SQL Server identifier naming. However in a particular work environment you might well use a naming convention which does include ID at the end of the column name in order to clearly make the intention of the column obvious.
To create a self-referencing foreign key you just do the same as normal which can be as part of the create table or an alter table.
CREATE TABLE pokemon (
pokemonId INT IDENTITY(1, 1),
...
CONSTRAINT fk_pokemon_evolvesFrom FOREIGN KEY (evolvesFrom) REFERENCES pokemon (pokemonId)
);
-- OR
ALTER TABLE pokemon
ADD CONSTRAINT fk_pokemon_evolvesFrom FOREIGN KEY (evolvesFrom)
REFERENCES pokemon (pokemonId)

SQL Creating New Table that is based of a column in an already created table

Here is an example of what I need, different values:
I already have table 1 created in the database.
Table 1: Person
Columns: PK->ID, Name, Favorite Color, Favorite Sport, etc..
This table is already in database and filled with values.
Now I want to create a second table, Table 2 which has a primary key of Favorite Sport column from my Table 1 and just one more column for the description.
Ex:
Table 2: Sports
Columns: Pk->Favorite Sport, description
I want to make sure I am just creating this table correctly, so I don't mess anything up. Would this be the correct syntax to use? (I will fill up the data separately after table is created.)
CREATE TABLE Sports (
Favorite_Sport Varcher(25),
Description Varcher(100),
PRIMARY KEY(Favorite_Sport),
Foreign KEY(Favorite_Sport) REFERENCES Person;
)
Thanks!
There are probably several ways to do this, but I think I'd go with
CREATE TABLE Sports
(SPORT Varchar2(25)
CONSTRAINT PK_SPORTS
PRIMARY KEY
USING INDEX,
Description Varchar2(100));
(I changed the name of the primary key column on the SPORTS table to SPORT).
You really don't want nor can you have SPORTS.SPORT reference PERSON.FAVORITE_SPORT, as FAVORITE_SPORT is not a primary or unique key on PERSON. Instead, you want the foreign key relationship to go the other way around, with PERSON.FAVORITE_SPORT referencing SPORTS.SPORT:
ALTER TABLE PERSON
ADD CONSTRAINT PERSON_FK1
FOREIGN KEY (FAVORITE_SPORT) REFERENCES SPORTS(SPORT);
SQLFiddle here
Best of luck.

How to reference a composite primary key into a single field?

I got this composite primary key in Table 1:
Table 1: Applicant
CreationDate PK
FamilyId PK
MemberId PK
I need to create a foreign key in Table 2 to reference this composite key. But i do not want to create three fields in Table 2 but to concatenate them in a single field.
Table 2: Sales
SalesId int,
ApplicantId -- This should be "CreationDate-FamilyId-MemberId"
What are the possible ways to achieve this ?
Note: I know i can create another field in Table 1 with the three columns concatenation but then i will have redundant info
What you're asking for is tantamount to saying "I want to treat three pieces of information as one piece of information without explicitly making it one piece of information". Which is to say that it's not possible.
That said, there are ways to make happen what you want to happen
Create a surrogate key (i.e. identity column) and use that as the FK reference
Create a computed column that is the concatenation of the three columns and use that as the FK reference
All else being equal (ease of implementation, politics, etc), I'd prefer the first. What you have is really a natural key and doesn't make a good PK if it's going to be referenced externally. Which isn't to say that you can't enforce uniqueness with a unique key; you can and should.

Database Design

I am making a webapp right now and I am trying to get my head around the database design.
I have a user model(username (which is primary key), password, email, website)
I have a entry model(id, title, content, comments, commentCount)
A user can only comment on an entry once. What is the best and most efficient way to go about doing this?
At the moment, I am thinking of another table that has username (from user model) and entry id (from entry model)
**username id**
Sonic 4
Sonic 5
Knuckles 2
Sonic 6
Amy 15
Sonic 20
Knuckles 5
Amy 4
So then to list comments for entry 4 it searches for id=4.
On a side note:
Instead of storing a commentCount, would it be better to calculate the comment count from the database each time when needed?
Your design is basically sound. Your third table should be named something like UsersEntriesComments, with fields UserName, EntryID and Comment. In this table, you would have a compound primary key consisting of the UserName and EntryID fields; this would enforce the rule that each user can comment on each entry only once. The table would also have foreign key constraints such that UserName must be in the Users table, and EntryID must be in the Entries table (the ID field, specifically).
You could add an ID field to the Users table, but many programmers (myself included) advocate the use of "natural" keys where possible. Since UserNames must be unique in your system, this is a perfectly valid (and easily readable) primary key.
Update: just read your question again. You don't need the Comments or the CommentsCount fields in your Entries table. Comments would properly be stored in the UsersEntriesComments table, and the counts would be calculated dynamically in your queries (saving you the trouble of updating this value yourself).
Update 2: James Black makes a good point in favor of not using UserName as the primary key, and instead adding an artificial primary key to the table (UserID or some such). If you use UserName as the primary key, allowing a user to change their user name is more difficult, as you have to change the username in all the related tables as well.
What exactly do you mean by
entry model(id, title, content, **comments**, commentCount)
(emphasis mine)? Since it looks like you have multiple comments per entity, they should be stored in a separate table:
comments(id, entry_id, content, user_id)
entry_id and user_id are foreign keys to respective tables. Now you just need to create a unique index on (entry_id, user_id) to ensure user can only add one comment per entity.
Also, you may want to create a surrogate (numeric, generated via sequence / identity) primary key for your users table instead of making user name your PK.
Here's my recommendation for your data model:
USERS table
USER_ID (pk, int)
USER_NAME
PASSWORD
EMAIL
WEBSITE
ENTRY table
ENTRY_ID (pk, int)
ENTRY_TITLE
CONTENT
ENTRY_COMMENTS table
ENTRY_ID (pk, fk)
USER_ID (pk, fk)
COMMENT
This setup allows an ENTRY to have 0+ comments. When a comment is added, the primary key being a composite key of ENTRY_ID and USER_ID means that the pair can only exist once in the table (IE: 1, 1 won't allow 1, 1 to be added again).
Do not store counts in a table - use a VIEW for that so the number can be generated based on existing data at the time of execution.
I wouldn't use the username as a primary ID. I would make a numeric id with autoincrement
I would use that new id in the relations table with a unique key on the 2 fields
Even though it isn't in the question, you may want to have a userid that is the primary key, otherwise it will be difficult if the user is allowed to change their username, or make certain people know you cannot change your username.
Make the joined table have a unique constraint on the userid and entryid. That way the database forces that there is only one comment/entry/user.
It would help if you specified a database, btw.
It sounds like you want to guarantee that the set of comments is unique with respect to username X post_id. You can do this by using a unique constraint, or if your database system doesn't support that explicitly, with an index that does the same. Here's some SQL expressing that:
CREATE TABLE users (
username VARCHAR(10) PRIMARY KEY,
-- any other data ...
);
CREATE TABLE posts (
post_id INTEGER PRIMARY KEY,
-- any other data ...
);
CREATE TABLE comments (
username VARCHAR(10) REFERENCES users(username),
post_id INTEGER REFERENCES posts(post_id),
-- any other data ...
UNIQUE (username, post_id) -- Here's the important bit!
);