Best way to change primary key of a huge table in SQL Server - 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.

Related

SQL Server error with Identity value when inserting row

Weird situation here. I'm inserting a row in a table with a primary key with IDENTITY (1,1), but the value that it uses is waaay wrong. This is the table:
CREATE TABLE [dbo].[adm_tm_modulo](
[id_modulo] [int] IDENTITY(1,1) NOT NULL,
[codigo] [varchar](255) NULL,
[descripcion] [varchar](255) NULL,
[estado] [int] NOT NULL,
[fecha_actualizacion] [datetime2](7) NULL,
[fecha_creacion] [datetime2](7) NULL,
[id_usuario_actualizacion] [int] NOT NULL,
[id_usuario_creacion] [int] NOT NULL,
[nombre] [varchar](255) NULL,
[ruta] [varchar](255) NULL,
PRIMARY KEY CLUSTERED
(
[id_modulo] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
Right now, it has 4 rows as you can see:
But whenever I insert a new row, the primary key ends up as if the IDENTITY value starts at 1000. This is what happens after I execute the INSERT:
I am certain that I have never inserted that many rows before, nor anyone else as this is a private DB in my own PC. Also, adding all the rows of all the tables, they are around 400 (not even close to 1000). And I tried inserting in other tables but the same thing is happening, only that in some tables it inserts a value from 3001 foward, or 4001, etc. It always starts with the first number after a thousand.
Any help about why this is happening would be very appreciated.
You may want to use a SEQUENCE object, which gives you more control on behavior of values generating.
https://learn.microsoft.com/en-us/sql/t-sql/statements/create-sequence-transact-sql?view=sql-server-ver15
Another option (which I prefer better) is to create an INSERT TRIGGER and define the logic you want in it

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

Can't create relationships in SQL Server diagram

I'm using SQL Server Management Studio 2008 R2 to create a database diagram. I've dropped in the tables that already have foreign key relationships; in addition, all the tables have primary keys. However, when I try to drag and drop from either table onto the other, I get the following error popup:
Primary key or UNIQUE constraint must be defined for table 'Results' before it can participate in a relationship.
Again, both tables have primary keys. The database was imported via Microsoft's SQL Server Migration Assistant for Access tool to preserve relationships and keys. What am I doing wrong?
Update: Here's the table:
CREATE TABLE [dbo].[Results](
[Result_AN] [int] IDENTITY(1,1) NOT NULL,
[Detail_AN] [int] NULL,
[Drug] [nvarchar](10) NULL,
[LDrug] [nvarchar](10) NULL,
[Lab_Result] [nvarchar](10) NULL,
[MRO_Result] [nvarchar](10) NULL,
[Confirm] [nvarchar](6) NULL,
[CutOff] [nvarchar](6) NULL,
[Quant] [nvarchar](10) NULL,
[Screen] [nvarchar](6) NULL,
[Unit] [nvarchar](6) NULL,
[Deleted] [bit] NULL,
[Scrn_Result] [nvarchar](10) NULL,
[SSMA_TimeStamp] [timestamp] NOT NULL,
CONSTRAINT [Results$PrimaryKey] PRIMARY KEY CLUSTERED
(
[Result_AN] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

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.

Unique constraint on multiple columns

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

Resources