A composite primary key is a primary key consisting of multiple columns. Microsoft usually refers to these as multicolumn primary keys in its documentation.
This article provides an example of creating a composite primary key using Transact-SQL in SQL Server.
You can create a composite primary key just as you would create a single primary key, except that instead of specifying just one column, you provide the name of two or more columns, separated by a comma.
Like this:
CONSTRAINT PK_Name PRIMARY KEY (Column1, Column2)
Example 1 – Create a Composite Primary Key
Here’s an example of a database using a composite primary key.
For the purposes of this example, I’ll create a database called PK_Test:
CREATE DATABASE PK_Test;
Now that the database is created, let’s go ahead and create the tables.
USE PK_Test; CREATE TABLE Musician ( MusicianId int NOT NULL, FirstName varchar(60), LastName varchar(60), CONSTRAINT PK_Musician PRIMARY KEY (MusicianID) ); CREATE TABLE Band ( BandId int NOT NULL, BandName varchar(255), CONSTRAINT PK_Band PRIMARY KEY (BandId) ); CREATE TABLE BandMember ( MusicianId int NOT NULL, BandId int NOT NULL, CONSTRAINT PK_BandMember PRIMARY KEY (MusicianID, BandId), CONSTRAINT FK_BandMember_Band FOREIGN KEY (BandId) REFERENCES Band(BandId), CONSTRAINT FK_BandMember_Musician FOREIGN KEY (MusicianId) REFERENCES Musician(MusicianId) );
In this example, the BandMember
table has a multicolumn primary key. In this case each column in the primary key is also a foreign key to the primary key of another table, but this is not a requirement.
The reasoning behind the above database design is that, a musician could potentially be a member of many bands. Also, each band can have many musicians. So we have a many-to-many relationship. This is why the BandMember
table is created – it is used as a cross-reference table between the Musician
table and the Band
table.
This particular case supports a composite primary key, because a musician being a member of a band should be a unique occurrence. In other words, we wouldn’t want multiple rows with a musician being a member of the same band. That would violate data integrity. It could also cause havoc when trying to maintain referential integrity in the even that we ever create a relationship between this table and another (which we do here).
Example 2 – Insert Data
Having just run the above code, I can now load the database with data:
INSERT INTO Musician VALUES ( 1, 'Ian', 'Paice' ), ( 2, 'Roger', 'Glover' ), ( 3, 'Richie', 'Blackmore' ), ( 4, 'Rod', 'Evans' ), ( 5, 'Ozzy', 'Osbourne' ); INSERT INTO Band VALUES ( 1, 'Deep Purple' ), ( 2, 'Rainbow' ), ( 3, 'Whitesnake' ), ( 4, 'Iron Maiden' ); INSERT INTO BandMember VALUES ( 1, 1 ), ( 1, 3 ), ( 2, 1 ), ( 2, 2 ), ( 3, 1 ), ( 3, 2 ), ( 4, 1 );
Example 3 – Basic Query
Now that data is in our database, let’s run a query to return some of that data.
Here’s a basic query:
SELECT CONCAT(m.FirstName, ' ', m.LastName) AS 'Musician', b.BandName AS 'Band' FROM Musician m JOIN BandMember bm ON m.MusicianId = bm.MusicianId JOIN Band b ON b.BandId = bm.BandId AND m.MusicianId = bm.MusicianId;
Result:
+------------------+-------------+ | Musician | Band | |------------------+-------------| | Ian Paice | Deep Purple | | Ian Paice | Whitesnake | | Roger Glover | Deep Purple | | Roger Glover | Rainbow | | Richie Blackmore | Deep Purple | | Richie Blackmore | Rainbow | | Rod Evans | Deep Purple | +------------------+-------------+
So as expected, this only returns those musicians and bands that have an entry in the BandMember
reference table.
Example 4 – Slightly Modified Query
Here’s a modified version of the above query that presents the results in a different way:
SELECT b.BandName AS 'Band', STRING_AGG(CONCAT(m.FirstName, ' ', m.LastName), ', ') AS 'Musicians' FROM Musician m JOIN BandMember bm ON m.MusicianId = bm.MusicianId JOIN Band b ON b.BandId = bm.BandId AND m.MusicianId = bm.MusicianId GROUP BY b.BandName;
Result:
+-------------+------------------------------------------------------+ | Band | Musicians | |-------------+------------------------------------------------------| | Deep Purple | Ian Paice, Roger Glover, Richie Blackmore, Rod Evans | | Rainbow | Roger Glover, Richie Blackmore | | Whitesnake | Ian Paice | +-------------+------------------------------------------------------+
Here the results are grouped by band, and all musicians for each band are all displayed as a comma separated list in a single field.
To do this I use the STRING_AGG()
function to concatenate the musicians.
Composite Foreign Key
The problem with the above example is that most of the data is out of date. Some of these musicians have actually left those bands. And some have left and then returned at a later date.
How can we deal with this?
We could create another reference table to record the time period each musician is a member of each band. Such a table would need to reference the BandMember
table via a foreign key. And seeing as this table has a composite primary key, we would need to use a composite foreign key on the new table that references it.
See How to Create a Composite Foreign Key in SQL Server for an example. That article uses the same example as above, except with an extra table with a composite foreign key that references the above composite primary key.