How can I change primary key on SQL Azure - azure-sql-database

I am going to change the primary key on SQL Azure. But it throws an error when using Microsoft SQL Server Management Studio to generate the scripts. Because every tables on SQL Azure must contains a primary key. And I can't drop it before create. What can I do if I must change it?
Script generated
IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[mytable]') AND name = N'PK_mytable')
ALTER TABLE [dbo].[mytable] DROP CONSTRAINT [PK_mytable]
GO
ALTER TABLE [dbo].[mytable] ADD CONSTRAINT [PK_mytable] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF)
GO
Error message
Msg 40054, Level 16, State 2, Line 3
Tables without a clustered index are not supported in this version of SQL Server. Please create a clustered index and try again.
Msg 3727, Level 16, State 0, Line 3
Could not drop constraint. See previous errors.
The statement has been terminated.
Msg 1779, Level 16, State 0, Line 3
Table 't_event_admin' already has a primary key defined on it.
Msg 1750, Level 16, State 0, Line 3
Could not create constraint. See previous errors.

I ran into this exact problem and contacted the Azure team on the forums. Basically it isn't possible. You'll need to create a new table and transfer the data to it.
What I did was create a transaction and within it do the following:
Renamed the old table to OLD_MyTable.
Create the new table with the correct Primary Key and call it MyTable.
Select the contents from OLD_MyTable
into MyTable.
Drop OLD_MyTable.
You may also need to call sp_rename on any constraints so they don't conflict.
See also: http://social.msdn.microsoft.com/Forums/en/ssdsgetstarted/thread/5cc4b302-fa42-4c62-956a-bbf79dbbd040

upgrade SQL V12 and heaps are supported on it. So you can drop the primary key and recreate it.

I appreciate that this may be late in the day for yourself, but it may help others.
I recently came across this issue and found the least painful solution was to download the database from Azure, restore it locally, update the primary key locally (as the key constraint is a SQL Azure specific issue), and then restore the database back into Azure.
This saved any issues in regards to renaming databases or transferring data between them.

You can try the following scripts. Change it to suit for your table def.
EXECUTE sp_rename N'[PK_MyTable]', N'[PK_MyTable_old]', 'OBJECT'
CREATE TABLE [dbo].[Temp_MyTable](
[id] [int] NOT NULL,
[text] [text] NOT NULL CONSTRAINT [PK_MyTable] PRIMARY KEY CLUSTERED (
[id] ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON))
INSERT INTO dbo.[Temp_MyTable] (Id, Text)
SELECT Id, Text FROM dbo.MyTable
drop table dbo.MyTable
EXECUTE sp_rename N'Temp_MyTable', N'MyTable', 'OBJECT'

This question is outdated because changing PK is already supported in latest version of SQL Azure. And you don't have to create temporary table.

Related

INSERT statement conflicted with the FOREIGN KEY constraint - SQL Server 2016

i have a problem, i exec to insert select a table and show me the error:
(Msg 547, Level 16, State 0, Line 4 The INSERT statement conflicted
with the FOREIGN KEY constraint "FK_art_sub_lin". The conflict
occurred in database "A_DEBQ_A", table "dbo.sub_lin". The statement
has been terminated.)
but the table dbo.sub_lin have 2 field when PK, Example:
CONSTRAINT [sub_lin_co_subl] PRIMARY KEY CLUSTERED
(
[co_subl] ASC,
[co_lin] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
the table art have:
(ALTER TABLE A_DEBQ_A.[dbo].[art] WITH CHECK ADD CONSTRAINT [FK_art_sub_lin] FOREIGN KEY([co_subl], [co_lin])
REFERENCES [dbo].[sub_lin] ([co_subl], [co_lin])
GO)
Rows to table dbo.sub_lin:
co_lin co_subl
AREN 222
PRSP 222
I don't know how to solve the error it gives me! Thanks for helping!
In your table A_DEBQ_A.[dbo].[art], it has a foreign key reference to another table. The way a FK works is it cannot have a value in that column that is not also in the primary key column of the referenced table.
If you have SQL Server Management Studio, open it up and sp_help 'A_DEBQ_A.[dbo].[art]' See which column that FK is on, and which column of which table it references. You're inserting some bad data.

SQL Server create primary key constraint duplicate key error

I have been experiencing some strange behaviour with one of my SQL commands taken from one of our stored procedures.
This command follows the below order of execution:
1) Drop table
2) Select * into table name from live server
3) Alter table to apply PK - this step fails once out of 4 daily executions
My SQL statement:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'
[inf].[tblBase_MyTable]') AND type in (N'U'))
DROP TABLE [inf].[tblBase_MyTable]
SELECT * INTO [inf].[tblBase_MyTable]
FROM LiveServer.KMS_ALLOCATION WITH (NOLOCK)
ALTER TABLE [inf].[tblBase_MyTable] ADD
CONSTRAINT [PK_KMS_ALLOCATION] PRIMARY KEY NONCLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY =
OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GRANT SELECT ON [inf].[tblBase_MyTable] TO ourGroup
This is very strange considering the table is dropped, and I thought the indexes / keys would also be dropped. However I get this error at the same time every day. Any advice would be very much appreciated.
Error:
The CREATE UNIQUE INDEX statement terminated because a duplicate key was found for the object name 'inf.tblBase_MyTable' and the index name 'PK_KMS_ALLOCATION'.
Duplicate keys in [inf].[tblBase_MyTable] table are actually possible thanks to the WITH (NOLOCK) hint which allows "dirty reads". Have a look at blog which describes this in detail: SQL Server NOLOCK Hint & other poor ideas:
What many people think NOLOCK is doing
Most people think the NOLOCK hint just reads rows & doesn’t have to
wait till others have committed their updates or selects. If someone
is updating, that is OK. If they’ve changed a value then 99.999% of
the time they will commit, so it’s OK to read it before they commit.
If they haven’t changed the record yet then it saves me waiting, its
like my transaction happened before theirs did.
The Problem
The issue is that transactions do more than just update the row. Often
they require an index to be updated OR they run out of space on the
data page. This may require new pages to be allocated & existing rows
on that page to be moved, called a PageSplit. It is possible for your
select to completely miss a number of rows &/or count other rows
twice.
Well... you might have to repeat creating the new table and filling it until the check-query from #DarkoMartinovic does not return duplicates. Only then you can continue to add the PK. But this solution might cause heavy load on your live system. And you nave no guarantee that you have a 1:1 copy of the data as well.
Having reviewed various helpful comments here, I have decided against (for now) implementing SNAPSHOT isolation as this interface does not make use of a proper staging environment.
To move to this would mean either creating a staging area and setting that database to READ COMMITTED SNAPSHOT isolation, and a rebuild of the entire interface.
To that end and on the basis of saving development time, we have opted for ensuring that any ghost reads where dupes could be brought across from the source are handled before applying the PK.
This is by no means an ideal solution in terms of performance on the target server but will provide some headroom for now and certainly remove the previous error.
SQL approach below:
DECLARE #ALLOCTABLE TABLE
(SEQ INT, ID NVARCHAR(1000), CLASSID NVARCHAR(1000), [VERSION] NVARCHAR(25), [TYPE]
NVARCHAR(100), VERSIONSEQUENCE NVARCHAR(100), VERSIONSEQUENCE_TO NVARCHAR(100),
BRANCHID NVARCHAR(100), ISDELETED INT, RESOURCE_CLASS NVARCHAR(25), RESOURCE_ID
NVARCHAR(100), WARD_ID NVARCHAR(100), ISCOMPLETE INT, TASK_ID NVARCHAR(100));
------- ALLOCATION
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[inf].
[tblBase_MyTable]') AND type in (N'U'))
DROP TABLE [inf].[tblBase_MyTable]
SELECT * INTO [inf].[tblBase_MyTable]
FROM LiveServer.KMS_ALLOCATION WITH (NOLOCK)
INSERT INTO #ALLOCTABLE
SELECT *
FROM
(SELECT
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ISCOMPLETE DESC) SEQ, AL.*
FROM [inf].[tblBase_MyTable] AL
)DUPS
WHERE SEQ >1
DELETE FROM [inf].[tblBase_MyTable]
WHERE ID IN (SELECT ID FROM #ALLOCTABLE)
AND ISCOMPLETE = 0
ALTER TABLE [inf].[tblBase_MyTable] ADD CONSTRAINT
[PK_KMS_ALLOCATION] PRIMARY KEY NONCLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GRANT SELECT ON [inf].[tblBase_MyTable] TO OurGroup

Create a database schema-script in ssms

I have a fully functional database in sql server. Around 40 tables. I have to install this schema (only the schema, not the data) on multiple other sql server instances. SSMS offers a nice way to auto generate schemas using Tasks --> Generate Scripts. It kinda works, but I am not sure if I understand it correctly:
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TableName]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[TableName](
[id] [uniqueidentifier] NOT NULL,
[history] [varchar](max) NOT NULL,
[isdeleted] [bit] NOT NULL,
CONSTRAINT [PK_RecGroupData] PRIMARY KEY CLUSTERED
(
[rid] 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]
END
GO
IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[DF_TableName_id]') AND type = 'D')
BEGIN
ALTER TABLE [dbo].[TableName] ADD CONSTRAINT [DF_TableName_id] DEFAULT (newid()) FOR [id]
END
--Just showing one ALTER TABLE and IF NOT EXISTS. The others are generated in the same way.
What happens, if I execute the script, create a new script with the exact same content, but add a new column to it (--> id, history, isdeleted and timestamp)? Does it automatically add the new line? I think yes, of course, but I don't get, how it would know, if the column should be NOT NULL, VARCHAR, BIT, or something similar. It would just execute
ALTER TABLE [dbo].[TableName] ADD CONSTRAINT [DF_TableName_id] DEFAULT (newid()) FOR [id]
(id => new sample column)
But there isn't any information about the data type or any other modifiers.
Also, if I execute my script like this one a second time, it'll throw some errors:
Meldung 1781, Ebene 16, Status 1, Zeile 3
An die Spalte ist bereits ein DEFAULT-Wert gebunden.
Which translates to this:
Message 1781, level 16, status 1, line 3
A DEFAULT value is already bound to the column.
Why does this happen?
The error message is saying that there was a default value assigned to that column before.
Also:
ALTER TABLE [dbo].[TableName] ADD CONSTRAINT [DF_TableName_id] DEFAULT (newid()) FOR [id]
is not the syntax for adding new column - this is to add default value of NEWID() to the column [id].
To add a column you you should follow this steps (with an example inside).
Also how would the SQL Server know the setup settings for the new columns from your manually added lines? It would simply allow you to define them as you want and accept if the syntax is right or through an error if not during the script parse process (can be done by [ctrl] + [F5] in SSMS).

Changing GUID Primary Keys to Integer Primary Keys

I took over an application a few months ago that used Guids for primary keys on the main tables. We've been having some index related database problems lately and I've just been reading up on the use of Guids as primary keys and I've learnt how much of a bad idea they can be, and I thought it might pay to look into changing them before the database gets too large.
I'm wondering if there's an easy way to change them to Ints? Is there some amazing software that will do this for me? Or is it all up to me?
I was thinking of just adding an extra Int column too all appropriate tables, write some code to poplate this column with 1 - n based on the CreationDate column, writing some more code to populate columns in all related tables, then switching the relationships to the new int columns. Does't sound TOO difficult... Would this be the best way to do it?
After combining pieces from all the above links, I came up with this script, simplified for the sake of the answer.
Tables Before Changes
JOB
Id Guid PK
Name nvarchar
CreationDate datetime
REPORT
Id Guid PK
JobId int
Name nvarchar
CreationDate datetime
SCRIPT
-- Create new Job table with new Id column
select JobId = IDENTITY(INT, 1, 1), Job.*
into Job2
from Job
order by CreationDate
-- Add new JobId column to Report
alter table Report add JobId2 int
-- Populate new JobId column
update Report
set Report.JobId2 = Job2.JobId
from Job2
where Report.JobId = Job2.Id
-- Delete Old Id
ALTER TABLE Job2 DROP COLUMN Id
-- Delete Relationships
ALTER TABLE Report DROP CONSTRAINT [FK_Report_Job]
ALTER TABLE Job DROP CONSTRAINT PK_Job
-- Create Relationships
ALTER TABLE [dbo].[Job2] ADD CONSTRAINT [PK_Job] PRIMARY KEY CLUSTERED
([JobId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
ALTER TABLE [dbo].[Report] WITH CHECK ADD CONSTRAINT [FK_Report_Job] FOREIGN KEY([JobId2])
REFERENCES [dbo].[Job2] ([JobId])
ON DELETE CASCADE
ALTER TABLE [dbo].[Report] CHECK CONSTRAINT [FK_Report_Job]
-- Rename Columns
sp_RENAME 'Report.JobId', 'OldJobId' , 'COLUMN'
sp_RENAME 'Report.JobId2', 'JobId' , 'COLUMN'
-- Rename Tables
sp_rename Job, Job_Old
sp_rename Job2, Job
I created the Job2 table because it means I didn't have to touch the original Job table (apart from deleting the relationships), so that everything could easily be put back to it's original state in case something went bad.

SQL Azure not recognizing my clustered Index

I get the following error when I try to insert a row into a SQL Azure table.
Tables without a clustered index are not supported in this version of
SQL Server. Please create a clustered index and try again.
My problem is I do have a clustered index on that table. I used SQL Azure MW to generate the Azure SQL Script.
Here's what I'm using:
IF EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[tblPasswordReset]') AND type in (N'U'))
DROP TABLE [dbo].[tblPasswordReset]
GO
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
IF NOT EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[tblPasswordReset]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[tblPasswordReset](
[PasswordResetID] [int] IDENTITY(1,1) NOT NULL,
[PasswordResetGUID] [uniqueidentifier] NULL,
[MemberID] [int] NULL,
[RequestDate] [datetime] NULL,
CONSTRAINT [PK_tblPasswordReset] PRIMARY KEY CLUSTERED
(
[PasswordResetID] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF)
)
END
GO
Why doesn't SQL Azure recognize my clustered Key? Is my script wrong?
Your script only creates the table if it did not exist yet. Perhaps there still is an old version of the table without a clustered index? You can check with:
select * from sys.indexes where object_id = object_id('tblPasswordReset')
If the table exists without the clustered index, you can add one like:
alter table tblPasswordReset add constraint
PK_tblPasswordReset primary key clustered
As far as I can see, your statement does conform to the Azure create table spec.
Be careful if you're using SSIS. I ran into this same problem, myself, but was using SSIS instead of manually inserting the data. By default SSIS will drop and recreate the table, so even though I had it properly defined with a clustered index, my SSIS script failed. On the "Edit Mappings" step in the SSIS wizard you can manually define the table creation script. I just deleted the table gen script there and my import worked.
(I'd leave this as a comment but my post count is too anemic)