Unique constraint on multiple columns - sql-server

CREATE TABLE [dbo].[user](
[userID] [int] IDENTITY(1,1) NOT NULL,
[fcode] [int] NULL,
[scode] [int] NULL,
[dcode] [int] NULL,
[name] [nvarchar](50) NULL,
[address] [nvarchar](50) NULL,
CONSTRAINT [PK_user_1] PRIMARY KEY CLUSTERED
(
[userID] ASC
)
) ON [PRIMARY]
GO
How do I add a unique constraint for columns fcode, scode, dcode with t-sql and/or management studio? fcode, scode, dcode must be unique together.

If the table is already created in the database, then you can add a unique constraint later on by using this SQL query:
ALTER TABLE dbo.User
ADD CONSTRAINT ucCodes UNIQUE (fcode, scode, dcode)

By using the constraint definition on table creation, you can specify one or multiple constraints that span multiple columns. The syntax, simplified from technet's documentation, is in the form of:
CONSTRAINT constraint_name UNIQUE [ CLUSTERED | NONCLUSTERED ]
(
column [ ASC | DESC ] [ ,...n ]
)
Therefore, the resuting table definition would be:
CREATE TABLE [dbo].[user](
[userID] [int] IDENTITY(1,1) NOT NULL,
[fcode] [int] NULL,
[scode] [int] NULL,
[dcode] [int] NULL,
[name] [nvarchar](50) NULL,
[address] [nvarchar](50) NULL,
CONSTRAINT [PK_user_1] PRIMARY KEY CLUSTERED
(
[userID] ASC
),
CONSTRAINT [UQ_codes] UNIQUE NONCLUSTERED
(
[fcode], [scode], [dcode]
)
) ON [PRIMARY]

This can also be done in the GUI. Here's an example adding a multi-column unique constraint to an existing table.
Under the table, right click Indexes->Click/hover New Index->Click Non-Clustered Index...
A default Index name will be given but you may want to change it. Check the Unique checkbox and click Add... button
Check the columns you want included
Click OK in each window and you're done.

USE [TSQL2012]
GO
/****** Object: Table [dbo].[Table_1] Script Date: 11/22/2015 12:45:47 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Table_1](
[seq] [bigint] IDENTITY(1,1) NOT NULL,
[ID] [int] NOT NULL,
[name] [nvarchar](50) NULL,
[cat] [nvarchar](50) NULL,
CONSTRAINT [PK_Table_1] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
CONSTRAINT [IX_Table_1] UNIQUE NONCLUSTERED
(
[name] ASC,
[cat] 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

Related

Removing a primary key but keeping foreign key?

Before anyone says this is a duplicate question and down-votes: I know how to solve the issue but I would like the advice on which is the best way to go about it.
I have made a booking system, where employees can create bookings. The employees table has a primary key of their clock number, this is a foreign key in the bookings table because employees can only delete their own bookings unless they are an administrator.
Now the problem occurs when I want to remove an employee, but they have made 1 or more bookings in the system, obviously as I'm deleting the 'parent' the 'child' will want to be removed as well but I need to keep the entire history of bookings.
The solutions I have is to remove the foreign key constraint in the bookings table, so there is still a clock number but not a foreign key. Or to set something up with ON CASCADE NULL feature?
CREATE TABLE [dbo].[Employees](
[ClockNo] [int] NOT NULL,
[Forename] [varchar](20) NOT NULL,
[Surname] [varchar](20) NOT NULL,
[Email] [varchar](50) NOT NULL,
[Department] [varchar](50) NOT NULL,
[IsAdmin] [bit] NOT NULL,
[Password] [varchar](max) NOT NULL,
CONSTRAINT [PK_Employees] PRIMARY KEY CLUSTERED
(
[ClockNo] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
CREATE TABLE [dbo].[Bookings](
[InvoiceNo] [varchar](40) NOT NULL,
[ClockNo] [int] NOT NULL,
[GateNo] [smallint] NOT NULL,
[TruckNo] [smallint] NOT NULL,
[StartTime] [datetime] NOT NULL,
[EndTime] [datetime] NOT NULL,
[Status] [varchar](20) NOT NULL,
[Seal] [varchar](40) NULL,
[ContainerNo] [varchar](40) NULL,
CONSTRAINT [PK_Bookings] PRIMARY KEY CLUSTERED
(
[InvoiceNo] ASC,
[GateNo] ASC,
[StartTime] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
If there is a way without doing any of these things that would be great, as I would like the clock number to stay even if the employee has been removed so we can see who had made the booking, even if they no longer work for us
Best approach would be to have a flag in the Employee table as isDeleted and use that flag to maintain deleted employees. That way you will have records of all the orders of the deleted employees as well

Multi-Table Foreign Key & Referential Integrity

I have a database where I'm trying to ensure referential integrity using foreign keys but I'm unsure how to proceed:
There are four tables: Company, Employee, Order and BillingCode.
A company has multiple employees, and each of those employees can place orders.
Each order must be allocated a billing code - which is taken from a lookup table.
Each company can have multiple billing codes.
An employee can only use a billing code thats allocated to their company.
Basic Diagram: http://i61.tinypic.com/21bm937.png
CREATE TABLE [dbo].[BillingCodes](
[BillingId] [int] NOT NULL,
[CompanyId] [int] NOT NULL,
[BillingCode] [varchar](10) NOT NULL,
CONSTRAINT [PK_BillingCodes] PRIMARY KEY CLUSTERED
(
[BillingId] 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
CREATE TABLE [dbo].[Company](
[CompanyId] [int] NOT NULL,
[CompanyName] [varchar](50) NOT NULL,
CONSTRAINT [PK_Company] PRIMARY KEY CLUSTERED
(
[CompanyId] 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
CREATE TABLE [dbo].[Employees](
[EmployeeId] [int] NOT NULL,
[CompanyId] [int] NOT NULL,
[Name] [varchar](50) NOT NULL
CONSTRAINT [PK_Employees] PRIMARY KEY CLUSTERED
(
[EmployeeId] 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
CREATE TABLE [dbo].[Orders](
[OrderId] [int] NOT NULL,
[EmployeeId] [int] NOT NULL,
[Price] [money] NOT NULL,
[Qty] [int] NOT NULL,
[BillingId] [int] NOT NULL,
CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED
(
[OrderId] 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].[BillingCodes] WITH CHECK ADD CONSTRAINT [FK_BillingCodes_Company] FOREIGN KEY([CompanyId])
REFERENCES [dbo].[Company] ([CompanyId])
GO
ALTER TABLE [dbo].[Employees] WITH CHECK ADD CONSTRAINT [FK_Employees_Company] FOREIGN KEY([CompanyId])
REFERENCES [dbo].[Company] ([CompanyId])
GO
ALTER TABLE [dbo].[Orders] WITH CHECK ADD CONSTRAINT [FK_Orders_Employees] FOREIGN KEY([EmployeeId])
REFERENCES [dbo].[Employees] ([EmployeeId])
GO
How do I ensure that [BillingId] in [Orders] is for the same company as the Employee?
Normally I'd just create a Foreign key between [BillingCodes] and [Orders] on [BillingId] and enforce the [CompanyId] reference in the business layer. Unfortunately in this instance I have a boss who likes to go into the database and correct data manually. So I need to enforce this in the database.
Is my only option to carry the CompanyID over to the orders table, and use a composite foreign key to [BillingCode]? This is a simplified version - there are another 3 tables between [Company] and [Employee], so I'd rather not have [CompanyId] in every table if I don't need to.
Is there an easier way to accomplish this?
An alternative is to add a trigger for insert and update on Orders. This would validate that the BillingId is linked to the same Company that the EnployeeId is. If they end up at different companies then raise an error. This is probably the way I'd do this.
You can do this using just referential integrity constraints, it just takes some redundancy -- which is not altogether a bad thing to have.
First, you have to bring the CompanyID along with the EmployeeID to the Orders table and make it referable to the Employee table.
alter table Employees add unique constraint UQ_Employee_Co( EmployeeID, CompanyID );
alter table BillingCodes add unique constraint UQ_BillingCodes_Company_Code( BillingID, CompanyID );
-- Or use indexes - no matter.
CREATE TABLE Orders(
OrderId int NOT NULL,
EmployeeId int NOT NULL,
CompanyID int not null,
Price money NOT NULL,
Qty int NOT NULL,
BillingId int NOT NULL,
CONSTRAINT PK_Orders PRIMARY KEY( OrderID ),
constraint FK_Orders_Employee foreign key( EmployeeID, CompanyID )
references Employees( EmployeeID, CompanyID ),
constraint FK_Orders_BillingCode foreign key( BillingID, CompanyID )
references BillingCodes( BillingID, CompanyID ),
);
Now you cannot have an order with an EmployeeID that associates with one company and a BillingID that associates with a different company because you cannot have an order with EmployeeID and CompanyID that don't match what's in the Employee table nor a CompanyID and BillingID that don't match what's in the BillingCodes table.
And all it costs you is one additional column.

Composite primary key with foreign key relationships to same table

so I have a table called "Event" and I want to create another table where an Event can contain more Events from the same table. This is what I have so far.
This is the current existing table...
CREATE TABLE [dbo].[EventEvents]
(
[step_id] [uniqueidentifier] NOT NULL PRIMARY KEY,
[title] [nvarchar](200) NOT NULL,
[Enabled] [bit] NOT NULL,
)
Then this is the table I am trying to create...
CREATE TABLE [dbo].[EventEvents]
(
[EventId] [uniqueidentifier] NOT NULL,
[EventChildId] [uniqueidentifier] NOT NULL,
[Enabled] [bit] NOT NULL,
CONSTRAINT [PK_EventEvents] PRIMARY KEY ([EventId], [EventChildId]),
CONSTRAINT [FK_Event_EventChild] FOREIGN KEY ([EventId],[EventChildId]) REFERENCES [dbo].[Event] ([step_id], [step_id])
)
So both EventId and EventChildId both are foreign keys to Event - step_id as 1 event can other events as children within it. But I need both EventId and EventChildId to be composite primary keys.
How can I do this?
At the moment I get an error saying:
Duplicate columns specified in FOREIGN KEY constraint key list
Thanks
I've figured it out, Thanks anyway.
CREATE TABLE [dbo].[EventChildren]
(
[EventId] [uniqueidentifier] NOT NULL,
[EventChildId] [uniqueidentifier] NOT NULL,
[Enabled] [bit] NOT NULL,
CONSTRAINT [EventEvents] PRIMARY KEY CLUSTERED
(
[EventId] ASC,
[EventChildId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[EventChildren] WITH CHECK ADD CONSTRAINT [FK_EventChildren_Event] FOREIGN KEY([EventId])
REFERENCES [dbo].[Event] ([step_id])
GO
ALTER TABLE [dbo].[EventChildren] WITH CHECK ADD CONSTRAINT [FK_EventChildren_EventChild] FOREIGN KEY([EventChildId])
REFERENCES [dbo].[Event] ([step_id])
GO
ALTER TABLE [dbo].[EventChildren] CHECK CONSTRAINT [FK_EventChildren_Event]
GO
ALTER TABLE [dbo].[EventChildren] CHECK CONSTRAINT [FK_EventChildren_EventChild]
GO

Best way to change primary key of a huge table in SQL Server

I have a table in a database with 580 million rows in it, and an awkward composite primary key. I would like to change the structure of the table to have an identity column as the primary key.
I am looking for some suggestions on the best possible way of doing this in the shortest amount of time.
We are using SQLServer 2008.
Current table structure:
CREATE TABLE [dbo].[POINTS_EARNED](
[CARD_ID] [int] NOT NULL,
[CYCLE_ID] [int] NOT NULL,
[POINTS_CODE] [int] NOT NULL,
[NO_POINTS] [int] NULL,
[ACCOUNT_ID] [int] NOT NULL,
[CREATED_DATE] [datetime] NULL,
[CREATED_BY] [varchar](20) NULL,
[LAST_MODIFIED_DATE] [datetime] NULL,
[LAST_MODIFIED_BY] [varchar](20) NULL,
[DELETED] [bit] NULL,
CONSTRAINT [PK_POINTS_EARNED] PRIMARY KEY CLUSTERED
(
[CARD_ID] ASC,
[CYCLE_ID] ASC,
[POINTS_CODE] ASC,
[ACCOUNT_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
New structure
CREATE TABLE [dbo].[POINTS_EARNED](
[Points_EARNED_ID] [int] [Identity] Primary Key,
[CARD_ID] [int] NOT NULL,
[CYCLE_ID] [int] NOT NULL,
[POINTS_CODE] [int] NOT NULL,
[NO_POINTS] [int] NULL,
[ACCOUNT_ID] [int] NOT NULL,
[CREATED_DATE] [datetime] NULL,
[CREATED_BY] [varchar](20) NULL,
[LAST_MODIFIED_DATE] [datetime] NULL,
[LAST_MODIFIED_BY] [varchar](20) NULL,
[DELETED] [bit] NULL
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
In the past we have tried 2 ways of doing this on other tables that needed to be restructured:
Create an empty table in the new structure
INSERT INTO
Newtable SELECT * FROM Oldtable
rename old table oldtable_bak
rename new table as old table
Add indexes, etc to new table
Unfortunately, with large tables this tends to cause SSMS to crash, so we have copied the data by changing step 2 to be:
bcp data out of the old table into a text file, and then bcp it back into the new table from the text file, which seems to work, but it takes several hours.
I'm interested to know whether there is a better, more efficient way of doing this.
I'm not sure if it is more efficient, but the following is "better" in a certain sense:
Insert existing data into a new table
Truncate existing table
Alter table to had identity primary key
Insert into new table
The reason this is better is because it preserves triggers, constraints, permissions, and other references to the table. That can be quite handy many applications.
As your implicitly point out, you might want to remove all indexes from the original table and add them after the data is re-inserted. Generally, building indexes all at once is more efficient.

Microsoft Entity Framework not showing foreign keys for some tables

I built the entity model from the database, using Entity Framework Version 5.0. The following DDL was used to create the tables:
CREATE TABLE [dbo].[Replenishment](
[replenishmentId] [int] IDENTITY(1,1) NOT NULL,
[locationID] [int] NOT NULL,
[inventoryItemId] [int] NOT NULL,
PRIMARY KEY CLUSTERED
([replenishmentId] 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].[Replenishment]
WITH CHECK ADD CONSTRAINT [FKReplenishm163678]
FOREIGN KEY([inventoryItemId])
REFERENCES [dbo].[InventoryItem] ([inventoryItemId])
GO
ALTER TABLE [dbo].[Replenishment] CHECK CONSTRAINT [FKReplenishm163678]
GO
ALTER TABLE [dbo].[Replenishment]
WITH CHECK ADD CONSTRAINT [FKReplenishm580804]
FOREIGN KEY([locationID])
REFERENCES [dbo].[Location] ([locationID])
GO
ALTER TABLE [dbo].[Replenishment] CHECK CONSTRAINT [FKReplenishm580804]
GO
When I build an entity model with the tables Replenishment, Location, and InventoryItem, all three tables show up in the model, but none of the relationships do.
Does anyone know why the foreign keys or the navigation properties don't appear?
============================================================================
Here is the DDL from the associated tables:
Here is the ddl from the other two tables:
CREATE TABLE [dbo].[Location](
[locationID] [int] IDENTITY(1,1) NOT NULL,
[addressID] [int] NULL,
[availabilityStatusID] [int] NOT NULL,
[inLocationId] [int] NULL,
[locationTypeID] [int] NOT NULL,
[locationName] [varchar](40) NOT NULL,
[locationDescription] [varchar](100) NOT NULL,
[locationAnchorX] [int] NULL,
[locationAnchorY] [int] NULL,
[locationImage] [varchar](255) NULL,
[diagramLayerId] [int] NULL,
[locationShapeParameters] [varchar](max) NULL,
[locationHeight] [int] NULL,
[locationWidth] [int] NULL,
[locationColor] [varchar](20) NULL,
PRIMARY KEY CLUSTERED
(
[locationID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[Location] WITH CHECK ADD CONSTRAINT [FKLocation209934] FOREIGN KEY([inLocationId])
REFERENCES [dbo].[Location] ([locationID])
GO
ALTER TABLE [dbo].[Location] CHECK CONSTRAINT [FKLocation209934]
GO
CREATE TABLE [dbo].[InventoryItem](
[inventoryItemId] [int] IDENTITY(1,1) NOT NULL,
[itemTaxonomyId] [int] NOT NULL,
[itemDescription] [varchar](100) NOT NULL,
[itemCode] [varchar](40) NULL,
[batchControlled] [bit] NOT NULL,
[assemblyDefinitionID] [int] NULL,
PRIMARY KEY CLUSTERED
(
[inventoryItemId] 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
SET ANSI_PADDING OFF
GO
I believe that I found the problem. I had been doing some work with SQL Server authentication and some work with windows Authentication. The information entered using Windows Authentication was not seen by my Visual Studio, which was connecting using SQL Server authentication.
After further research I found that this was not the case. I am still bemused by this.

Resources