SQL foreign key constraint with additional column value check - sql

I have this situation
CREATE TABLE [dbo].[Customer]
(
Id INT IDENTITY(1,1) NOT NULL,
IsWholesaler BIT NOT NULL,
Name NVARCHAR(255) NOT NULL
)
CREATE TABLE [dbo].[WholesalerUser]
(
Id INT IDENTITY(1,1) NOT NULL,
CustomerId INT NOT NULL,
UserId INT NOT NULL
)
CREATE TABLE [dbo].[User]
(
Id INT IDENTITY(1,1) NOT NULL,
Name NVARCHAR(255)
)
One Customer which has IsWholesaler column set to true can have multiple users.
I have WholesalerUser table for this purpose but the Foreign key WholesalerUser.CustomerId is attached to Customer.Id but how to check the column IsWholesaler = 1 ?

Foreign key can be defined over several columns at once.
Add a column IsWholesaler to the WholesalerUser.
Add a check constraint to make sure that it is always 1.
Define foreign key over two columns (CustomerId, IsWholesaler).
To define such foreign key the Customer table should have either primary key as (ID, IsWholesaler), or a separate unique index on (ID, IsWholesaler).
Table Customer
CREATE TABLE [dbo].[Customer](
[Id] [int] IDENTITY(1,1) NOT NULL,
[IsWholesaler] [bit] NOT NULL,
[Name] [nvarchar](255) NOT NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED
(
[Id] ASC
))
Unique index to support foreign key constraint:
CREATE UNIQUE NONCLUSTERED INDEX [IX_ID_IsWholesaler] ON [dbo].[Customer]
(
[Id] ASC,
[IsWholesaler] ASC
)
Table WholesalerUser
CREATE TABLE [dbo].[WholesalerUser](
[Id] [int] IDENTITY(1,1) NOT NULL,
[CustomerId] [int] NOT NULL,
[IsWholesaler] [bit] NOT NULL,
[UserId] [int] NOT NULL,
CONSTRAINT [PK_WholesalerUser] PRIMARY KEY CLUSTERED
(
[Id] ASC
))
Foreign key
ALTER TABLE [dbo].[WholesalerUser] WITH CHECK
ADD CONSTRAINT [FK_WholesalerUser_Customer]
FOREIGN KEY([CustomerId], [IsWholesaler])
REFERENCES [dbo].[Customer] ([Id], [IsWholesaler])
GO
ALTER TABLE [dbo].[WholesalerUser]
CHECK CONSTRAINT [FK_WholesalerUser_Customer]
GO
Check constraint
ALTER TABLE [dbo].[WholesalerUser] WITH CHECK
ADD CONSTRAINT [CK_WholesalerUser]
CHECK (([IsWholesaler]=(1)))
GO
ALTER TABLE [dbo].[WholesalerUser]
CHECK CONSTRAINT [CK_WholesalerUser]
GO
ALTER TABLE [dbo].[WholesalerUser]
ADD CONSTRAINT [DF_WholesalerUser_IsWholesaler]
DEFAULT ((1)) FOR [IsWholesaler]
GO

Related

No Primary or candidate keys in the referenced table that match column list in foreign key

I am trying to create a few basic SQL tables however I keep getting the following error:
There are no primary or candidate keys in the referenced table 'CART' that match the referencing column list in the foreign key 'FK__ORDERS__CART_ID__2B3F6F97'
This is the current code which I am using.
create table USERS
(
User_ID int NOT NULL primary key,
Address varchar(30) NOT NULL,
Email varchar(30) NOT NULL,
Password varchar(30) NOT NULL,
Phone varchar(30) NOT NULL,
F_Name varchar(30) NOT NULL,
L_Name varchar(30) NOT NULL,
Date_of_Birth varchar(30) NOT NULL
)
create table PAYMENT
(
User_ID int NOT NULL primary key,
Credit_Card varchar(30) NOT NULL,
Debit_Card varchar(30) NOT NULL,
Google_Pay varchar(30) NOT NULL,
Apple_Pay varchar(30) NOT NULL,
Paypal varchar(30) NOT NULL,
foreign key (User_ID)
references USERS(User_ID)
)
create table CART
(
User_ID int NOT NULL,
Cart_ID int NOT NULL,
Total_Price float NOT NULL,
primary key (Cart_ID, User_ID),
foreign key(User_ID)
references USERS(User_ID)
)
create table ORDERS
(
Order_ID int NOT NULL primary key,
Total_Price float NOT NULL,
Payment_Method varchar(30) NOT NULL,
User_ID int NOT NULL,
CART_ID int NOT NULL,
foreign key (User_ID)
references USERS(User_ID),
foreign key (Cart_ID)
references CART(Cart_ID),
)
create table ORDER_HISTORY
(
User_ID int NOT NULL,
Order_ID int NOT NULL,
primary key (User_ID, Order_ID),
foreign key (User_ID)
references USERS(User_ID),
foreign key (Order_ID)
references ORDERS(Order_ID)
)
I have tried modifying things and moving them around but cannot get the error to go away. I suspect that this is probably quite simple and obvious but since I am so new to SQL I am probably missing it.
cart has a composite primary key (cart_id, order_id). Each of these columns aren't primary keys independently, just their combination. The foreign key from orders should act the same - you should have a single foreign key based on the combination of the two:
create table ORDERS
(
Order_ID int NOT NULL primary key,
Total_Price float NOT NULL,
Payment_Method varchar(30) NOT NULL,
User_ID int NOT NULL,
CART_ID int NOT NULL,
foreign key (User_ID)
references USERS(User_ID),
foreign key (Cart_ID, User_id) -- here
references CART(Cart_ID, User_id),
)
As table CART has composite primary key, then you should to reference to a composite primary key:
create table ORDERS
(
Order_ID int NOT NULL primary key,
Total_Price float NOT NULL,
Payment_Method varchar(30) NOT NULL,
User_ID int NOT NULL,
CART_ID int NOT NULL,
foreign key (User_ID)
references USERS(User_ID),
foreign key (Cart_ID, User_ID)
references CART(Cart_ID, User_ID),
)
In table Cart, you need to define Cart_ID with PRIMARY KEY. If for some reason you cannot or don't want to define a PRIMARY KEY then try UNIQUE KEY constraint on the same column.
It is the Primary Key on the Cart table.
CREATE TABLE [dbo].[CART](
[User_ID] [int] NOT NULL,
[Cart_ID] [int] NOT NULL,
[Total_Price] [float] NOT NULL,
CONSTRAINT [PK__CART__D6AB58B9B18E85A5] PRIMARY KEY CLUSTERED
(
[User_ID] ASC,
[Cart_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[CART] WITH CHECK ADD FOREIGN KEY([User_ID])
REFERENCES [dbo].[USERS] ([User_ID])

Audit history of sql child table

I'm recording all insert and update on TaskDetail table using a trigger,Now I want to assign multiple staff to a task, But if staff id stored in different child table how can I track audit history, I have considered storing staff id as comma separated values but child table is always a good option.
In TaskStaff table multiple staff will have same taskId
CREATE TRIGGER [dbo].[TaskDetail_History_Trigger]
ON [dbo].[TaskDetail]
FOR Insert,UPDATE
AS
INSERT INTO TaskHistory SELECT * FROM inserted
GO
ALTER TABLE [dbo].[ProductionDetail] ENABLE TRIGGER [Task_History_Trigger]
GO
CREATE TABLE [dbo].[TaskDetail](
[Id] [int] IDENTITY(1,1) NOT NULL,
[StaffId] [int] NULL,
CONSTRAINT [PK_ProductionDetail_1] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
CREATE TABLE [dbo].[TaskHistory](
[HistoryId] [int] IDENTITY(1,1) NOT NULL,
[Id] [int] NOT NULL,
[StaffId] [int] NULL,
CONSTRAINT [PK_ProductionDetail_1] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
CREATE TABLE [dbo].[TaskStaff](
[Id] [int] IDENTITY(1,1) NOT NULL,
[TaskId] [int] NOT NULL,
[StaffId] [int] NOT NULL,
CONSTRAINT [PK_ProductionDetailStaff] PRIMARY KEY CLUSTERED
(
[Id] ASC
)

Filtered Constraint When Defining a SQL Table?

I wanted to set a unique constraint that allowed for nulls. I came up with filtered constraints. Great. Except... they're all post-table creation. As in:
Step 1: define a table
Step 2: add the constraint
Like this:
CREATE TABLE MyTable
(
[Id] INT NOT NULL,
[ColumnA] INT NULL,
[ColumnB] INT NULL,
CONSTRAINT [PK_MyTable] PRIMARY KEY ([ID] ASC)
)
CREATE UNIQUE INDEX [MyNullableIndex] ON MyTable (ColumnA, ColumnB) WHERE ColumnA IS NOT NULL AND ColumnB IS NOT NULL
I want to do this in one fell swoop. As in: include the constraint in the table definition. Is this possible?
Something like this:
CREATE TABLE MyTable
(
[Id] INT NOT NULL,
[ColumnA] INT NULL,
[ColumnB] INT NULL,
CONSTRAINT [PK_MyTable] PRIMARY KEY ([ID] ASC),
CONSTRAINT [Unique_ColumnA_ColumnB] UNIQUE (ColumnA, ColumnB) WHERE ColumnA IS NOT NULL AND ColumnB IS NOT NULL
)
Or is there some kind of fancy check constraint I can use?
Thanks in advance.
e.g. from here:
CREATE TABLE MyTable
(
[Id] INT NOT NULL,
[ColumnA] INT NULL,
[ColumnB] INT NULL,
CONSTRAINT [PK_MyTable] PRIMARY KEY ([ID] ASC),
CONSTRAINT [Unique_ColumnA_ColumnB] UNIQUE (ColumnA, ColumnB)
)

Foreign key refers invalid column in referred table

I have a table ClientsPurchases with the column BillNo. I would like to refer BillNo column into Payments table as a Foreign Key, but its showing error.
There are no primary or candidate keys in the referred table.
'ClientsPurchases' that match the referencing column list in the
foreign key 'FK__Payments__BillNo__286302EC'. Msg 1750, Level 16,
State 0, Line 1
CREATE Table ClientsPurchases
(
PurchasesId int IDENTITY(1,1) PRIMARY KEY NOT NULL,
PurchasesDetails VARCHAR(75),
[BillNo] varchar(75) NULL
)
--My Payments table as bellow
CREATE TABLE [dbo].[Payments]
(
[PaymentId] [int] IDENTITY(1,1) NOT NULL,
[PayAmount] [decimal](18, 0) NULL,
[PaymentDate] [datetime] NULL,
[ClinetId] [int] NULL,
FOREIGN KEY ([BillNo]) REFERENCES ClientsPurchases(BillNo)
)
Please advice.
The [BillNo] in your ClientsPurchases should be unique.
Also, I think your foreign key should be defined as a column in the Payments table as well:
CREATE TABLE [dbo].[Payments]
(
[PaymentId] [int] IDENTITY(1,1) NOT NULL,
[PayAmount] [decimal](18, 0) NULL,
[PaymentDate] [datetime] NULL,
[ClinetId] [int] NULL,
[BillNo] [int] NOT NULL,
FOREIGN KEY ([BillNo]) REFERENCES ClientsPurchases(BillNo)
)
More about creating foreign key constraints here.
You cannot refer to BillNo because it isn't a key. You should make it NOT NULL and UNIQUE.

Inserting into many-to-many table in SQL Server

This is my Tag table:
CREATE TABLE [dbo].[Tag](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NULL,
[CreationDate] [datetime] NOT NULL,
[TagSlug] [nvarchar](max) NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
and this is my Post table:
CREATE TABLE [dbo].[Post](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Title] [nvarchar](400) NOT NULL,
[Body] [nvarchar](max) NOT NULL,
[Summary] [nvarchar](max) NOT NULL,
[CreationDate] [datetime] NOT NULL,
[UrlSlug] [nvarchar](max) NOT NULL,
[Picture] [nvarchar](max) NULL,
[TagId] [int] NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[Post] WITH CHECK ADD CONSTRAINT [Post_Tag] FOREIGN KEY([TagId])
REFERENCES [dbo].[Tag] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Post] CHECK CONSTRAINT [Post_Tag]
GO
I just wanna to insert the Id from Tag and PostId from Post into a new table named Post_Tag which is a many to many relation, this is the script of my Post_Tag table:
CREATE TABLE [dbo].[Post_Tag](
[PostId] [int] NOT NULL,
[TagId] [int] NOT NULL,
CONSTRAINT [PK_dbo.Post_Tag] PRIMARY KEY CLUSTERED ([PostId] ASC, [TagId] ASC)
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Post_Tag] WITH CHECK
ADD CONSTRAINT [FK_dbo.Post_Tag_dbo.Post_PostId]
FOREIGN KEY([PostId]) REFERENCES [dbo].[Post] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Post_Tag] CHECK CONSTRAINT [FK_dbo.Post_Tag_dbo.Post_PostId]
GO
ALTER TABLE [dbo].[Post_Tag] WITH CHECK
ADD CONSTRAINT [FK_dbo.Post_Tag_dbo.Tag_TagId]
FOREIGN KEY([TagId]) REFERENCES [dbo].[Tag] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Post_Tag] CHECK CONSTRAINT [FK_dbo.Post_Tag_dbo.Tag_TagId]
GO
Now, to do that I've tried the below query:
insert into [Blog].[dbo].[Post_Tag] (PostId,TagId)
select [Id] as [PostId] from [OldBlog].[dbo].[Tag]
select [TagId] from [OldBlog].[dbo].[Post]
but this error appear while running the script:
The select list for the INSERT statement contains fewer items than the insert list. The number of SELECT values must match the number of INSERT columns.
what's wrong with my query? thanks
The 2 select queries are being processed separately. You will have to come up with a way to join [OldBlog].[dbo].[Tag] to [OldBlog].[dbo].[Post] so you can insert fields PostId,TagId into [Blog].[dbo].[Post_Tag] from this new table expression.
For this, you can use the row number of each row from the two select statements as a link so you can join them and select what you need from both of them.
SELECT POST.[PostId], TAG.[TagId]
FROM (
select ROW_NUMBER() OVER (ORDER BY [Id]) AS Link, [Id] as [PostId] from [OldBlog].[dbo].[Tag]) AS POST
JOIN (
select ROW_NUMBER() OVER (ORDER BY [TagId]) AS Link, [TagId] from [OldBlog].[dbo].[Post]) AS TAG ON POST.Link = TAG.Link
IMPORTANT NOTE:
This is just a means of "forcing" a relationship between tables without any relationship to each other whatsoever. This is indeed a dangerous thing to do because we are forcing a relationship between the tables based on row number and not an actual key. This should only be used if there is no definite expected output or as a last resort if there is no other way to link two or more unrelated tables where the relationship of each selected column don't matter.