Two Composite Primary Key in SQL - sql

I am trying to define table as follows:
CREATE TABLE dbo.[User]
(
Id int NOT NULL IDENTITY PRIMARY KEY,
Name nvarchar(1024) NOT NULL
);
CREATE TABLE [Group]
(
Id int NOT NULL IDENTITY PRIMARY KEY,
Name nvarchar(1024) NOT NULL
);
CREATE TABLE [UserToGroup]
(
Name VARCHER(20)
UserId int NOT NULL,
GroupId int NOT NULL,
PRIMARY KEY CLUSTERED ( UserId, Name),
PRIMARY KEY CLUSTERED ( GroupId, Name),
FOREIGN KEY ( UserId ) REFERENCES [User] ( Id ) ON UPDATE NO ACTION ON DELETE CASCADE,
FOREIGN KEY ( GroupId ) REFERENCES [Group] ( Id ) ON UPDATE NO ACTION ON DELETE CASCADE
);
How can i create table with two Composite Primary Key?

Name VARCHAR(20) NOT NULL,
UserId int NOT NULL,
GroupId int NOT NULL,
UNIQUE ( UserId, Name),
UNIQUE ( GroupId, Name)
In the relational model and in SQL there is no logical difference between one key and another so there's no very strong reason to have a different syntax for specifying one key over any other. However, for better or worse, the authors of the SQL standard decided to make a limitation that the PRIMARY KEY constraint syntax can only be used once per table and that where you need more than one key you have to use one or more UNIQUE constraints instead. Arguably it would be desirable to drop that limitation but since it's fundamentally just a bit of syntactical sugar that's unlikely to happen any time soon.

Related

Prevent multiple foreign key references to same row in primary table

I have a table containing Employees all of whom have an ID; I'm referencing this ID in two other tables (Salesman, Mechanic) via a foreign key. What I want to ensure is that an employee is either a mechanic or a salesman, but never both. In other words, I want to associate this logic with my foreign keys:
How would I integrate logic like that into a table? I'm a SQL beginner, so I apologize if this is a dumb question.
These are my tables:
CREATE TABLE [dbo].[Employee]
(
Number INT NOT NULL,
-- ...
PRIMARY KEY(Number)
);
CREATE TABLE [dbo].[Salesman]
(
ID INT NOT NULL,
-- ...
PRIMARY KEY(ID),
FOREIGN KEY(ID) REFERENCES [dbo].[Employee](ID)
);
CREATE TABLE [dbo].[Mechanic]
(
ID INT NOT NULL,
-- ...
PRIMARY KEY(ID),
FOREIGN KEY(ID) REFERENCES [dbo].[Employee](ID)
);
This is a one-of relationship and it is tricky to implement in SQL. Here is one method:
CREATE TABLE [dbo].[Employee] (
EmployeeId INT PRIMARY KEY,
EmployeeType VARCHAR(32)
-- ...
CHECK (EmployeeType IN ('Mechanic', 'SalesPerson')),
UNIQUE (EmployeeType, EmployeeId)
);
CREATE TABLE [dbo].[SalesPerson]
(
EmployeeId INT PRIMAY KEY,
-- ...
EmployeeType as (CONVERT(VARCHAR(32), 'Salesperson')) PERSISTED,
FOREIGN KEY (EmployeeType, EmployeeId) REFERENCES [dbo].[Employee](EmployeeType, EmployeeId)
);
CREATE TABLE [dbo].[Mechanic] (
EmployeeId INT PRIMARY KEY,
EmployeeType as (CONVERT(VARCHAR(32), 'Mechanic')) PERSISTED,
-- ...
FOREIGN KEY (EmployeeType, EmployeeId) REFERENCES [dbo].[Employee](EmployeeType, EmployeeId)
);
Here is a SQL Fiddle illustrating the code.

Create a scheme for Packages, Products and Services

I have 3 tables: Packages, Products and Services:
create table dbo.Packages (
Id int identity not null
Name nvarchar (80) not null
)
create table dbo.Products (
Id int identity not null
Name nvarchar (80) not null
)
create table dbo.Services (
Id int identity not null
Name nvarchar (80) not null
)
I need to relate the 3 tables to fulfill the following requirements:
A package consists of a mix of products and services;
All products can be included in a package;
Not all services can be included in a package;
A product or service can be included in many packages;
A product or a service cannot appear twice in the same package.
I am not sure how to build this scheme specially because of (3) and (5).
Probably I will need Table inheritance, Many to Many relationship and some kind of Index for (5)?
Could someone, please, advice me on this?
UPDATE
One option to Frazz suggestion would be to use Component base table as follows so a Service can have the ComponentId as null. Just one more option ...
CREATE TABLE dbo.Packages (
id INT IDENTITY NOT NULL,
name NVARCHAR(80) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE dbo.Components (
id INT IDENTITY NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE dbo.Products (
id INT IDENTITY NOT NULL,
component_id INT NOT NULL,
name NVARCHAR(80) NOT NULL
PRIMARY KEY (id)
);
CREATE TABLE dbo.Services (
id INT IDENTITY NOT NULL,
component_id INT NULL,
name NVARCHAR(80) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE dbo.Package_Components (
package_id INT NOT NULL REFERENCES Packages(id),
component_id INT NOT NULL REFERENCES Components(id)
PRIMARY KEY (package_id, component_id)
);
The following should solve most of what you are asking (tested on SQLFiddle):
CREATE TABLE dbo.Packages (
id INT IDENTITY NOT NULL,
name NVARCHAR(80) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE dbo.Products (
id INT IDENTITY NOT NULL,
name NVARCHAR(80) NOT NULL
PRIMARY KEY (id)
);
CREATE TABLE dbo.Services (
id INT IDENTITY NOT NULL,
name NVARCHAR(80) NOT NULL,
can_be_packaged BIT NOT NULL
PRIMARY KEY (id)
);
CREATE TABLE dbo.Package_Products (
package_id INT NOT NULL REFERENCES Packages (id),
product_id INT NOT NULL REFERENCES Products (id)
PRIMARY KEY (package_id, product_id)
);
CREATE TABLE dbo.Package_Services (
package_id INT NOT NULL REFERENCES Packages (id),
service_id INT NOT NULL REFERENCES Services (id)
PRIMARY KEY (package_id, service_id)
);
The two additional tables Package_Products and Package_Services implement the many to many relationships you need for (1), (2) and (4). Their primary keys enforce (5).
What is left is your requirement (3). You said that this depends exclusively on the service, so the additional BIT column can_be_packaged is a good way to handle the requirement. But then you need to enforce it.
The problem is that you cannot execute an SQL statement in a CHECK constraint. So the only solution I see is to have a TRIGGER FOR INSERT and a TRIGGER FOR UPDATE on the Package_Services table. These triggers should execute a SELECT on the Services table and check that the can_be_packaged bit is 1.

Use a common table with many to many relationship

I have two SQL tables: Job and Employee. I need to compare Job Languages Proficiencies and Employee Languages Proficiencies. A Language Proficiency is composed by a Language and a Language Level.
create table dbo.EmployeeLanguageProficiency (
EmployeeId int not null,
LanguageProficiencyId int not null,
constraint PK_ELP primary key clustered (EmployeeId, LanguageProficiencyId)
)
create table dbo.JobLanguageProficiency (
JobId int not null,
LanguageProficiencyId int not null,
constraint PK_JLP primary key clustered (JobId, LanguageProficiencyId)
)
create table dbo.LanguageProficiency (
Id int identity not null
constraint PK_LanguageProficiency_Id primary key clustered (Id),
LanguageCode nvarchar (4) not null,
LanguageLevelId int not null,
constraint UQ_LP unique (LanguageCode, LanguageLevelId)
)
create table dbo.LanguageLevel (
Id int identity not null
constraint PK_LanguageLevel_Id primary key clustered (Id),
Name nvarchar (80) not null
constraint UQ_LanguageLevel_Name unique (Name)
)
create table dbo.[Language]
(
Code nvarchar (4) not null
constraint PK_Language_Code primary key clustered (Code),
Name nvarchar (80) not null
)
My question is about LanguageProficiency table. I added an Id has PK but I am not sure this is the best option.
What do you think about this scheme?
Your constraint of EmployeeId, LanguageProficiencyId allows an employee to have more than one proficiency per language. This sounds counterintuitive.
This would be cleaner, as it allows only one entry per language:
create table dbo.EmployeeLanguageProficiency (
EmployeeId int not null,
LanguageId int not null,
LanguageLevelId int not null,
constraint PK_ELP primary key clustered (EmployeeId, LanguageId)
)
I don't see the point of table LanguageProficiency at the moment.
Same applies to the Job of course. Unless you would like to allow a "range" of proficiencies. But assuming that "too high proficiency" does not hurt, it can easilly be defined through a >= statement in our queries.
Rgds

SQL Server - Create an identifying relationship

I'm currently designing a database to be implemented in SQL Server. I created the following tables without problem:
CREATE TABLE [Client] (
[ClientId] INT NOT NULL,
[Name] VARCHAR(45) NOT NULL,
[IsEnabled] BIT NOT NULL DEFAULT 1,
CONSTRAINT PK_TCASystem PRIMARY KEY CLUSTERED (
ClientId
)
);
CREATE TABLE [Configuration] (
[ConfigId] INT NOT NULL,
[ClientId] INT NOT NULL,
[Name] VARCHAR(45) NOT NULL,
CONSTRAINT PK_Configuration PRIMARY KEY CLUSTERED (
ConfigId, ClientId
),
CONSTRAINT "FK_SystemConfiguration" FOREIGN KEY
(
ClientId
) REFERENCES [Client] (
ClientId
)
);
However, when I tried to add this one:
CREATE TABLE [Mail] (
[MailId] INT NOT NULL,
[ConfigId] INT NOT NULL,
[Recipient] VARCHAR(500) NOT NULL,
[Sender] VARCHAR(50) NOT NULL,
[Subject] VARCHAR(250) NOT NULL,
[Message] TEXT NULL,
CONSTRAINT PK_Mail PRIMARY KEY CLUSTERED (
MailId, ConfigId
),
CONSTRAINT "FK_ConfigurationMail" FOREIGN KEY
(
ConfigId
) REFERENCES [Configuration] (
ConfigId
)
);
I got an error saying that There are no primary or candidate keys in the referenced table 'Configuration' that match the referencing column list in the foreign key 'FK_ConfigurationMail'. I believe this is because the constraint is trying to reference ConfigId, only one half of the composite key, and for this to work I'd need to reference the ClientId too, is that correct?
But my problem is that I first did the design for this database in MYSQL Workbench, and there I indicated that Configuration and Mail, as well as Client and Configuration, have a 1:n identifying relationship (because a Mail instance cannot be created if there isn't a Configuration instance first, and at the same time a Configuration instance cannot exist without having being assigned to a Client first), and as such it created the composite keys for Configuration and Mail. You can see a picture of that here.
So my question is, how can I translate this identifying relationship to SQL Server? Or is that not possible?
EDIT: As suggested I will remove the composite keys from the Configuration table, albeit my question still stands: If I have a 1:n identifying relationship where one of the tables involved uses composite keys, how can I display this on SQL Server? Or is such a case never supposed to happen?
2ND EDIT: To anyone who might come across this question, this post is well worth a read. Cleared up all my confusion in the matter.
Foreign key must reference PK (the entire PK, not portion of PK) or unique index. So add this between create table [Configuration] and [Mail].
CREATE UNIQUE NONCLUSTERED INDEX [UX_Configuration] ON [Configuration]
(
[ConfigId] ASC
)
Check out at sql fiddle for the whole working script:
http://www.sqlfiddle.com/#!3/8877f

SQL Multiple Foreign Keys as Primary Keys

If I declare the table below does it implicitly imply that both the foreign keys make a unique primary key or do I need to do something more to make both attributes as a primary key?
CREATE TABLE Report_has_Items
(
ReportID int REFERENCES Report(ReportID) NOT NULL,
ItemID int REFERENCES Item(ItemID) NOT NULL
)
Essentially both attributes which are foreign keys from other tables, together would form a unique key.
No it doesn't. The above table has no primary key. If you want to use the fields as a primary key use:
CREATE TABLE Report_has_Items(
ReportID int REFERENCES Report(ReportID) NOT NULL,
ItemID int REFERENCES Item(ItemID) NOT NULL,
PRIMARY KEY (ReportID, ItemID)
)
or something similar depending on your sql dilect.
Let's name our constraints, eh?
CREATE TABLE dbo.Report_has_Items(
ReportID int NOT NULL,
CONSTRAINT [FK_RHI_Report] (ReportId) REFERENCES dbo.Report(ReportID),
ItemID int NOT NULL,
Constraint [FK_RHI_Item] (ItemId) REFERENCES dbo.Item(ItemID),
CONSTRAINT [PK_RHI] PRIMARY KEY (ReportID, ItemID)
)
I am not sure i understand your question completely but i assume you are trying to create a composite primary key (primary key with more than one attribute). You could do the following.
CREATE TABLE Report_has_Items(
ReportID int references Report(ReportID),
ItemID int references Item(ItemID),
PRIMARY KEY (ReportID , ItemID )
);
Note:The pair (ReportID , ItemID ) must then be unique for the table and neither value can be NULL.
Here is a very useful link for SQL Queries