Note: as of Azure SQL Database v12, these restrictions no longer apply.
The is no such thing as a 'primary index'. There is such a thing as a 'primary key' and also there is such a thing as a 'clustered index'. Distinct concepts, often confused. With this distinction in mind, lets revisit the question:
Q1) Can the clustered index in a SQL Azure table be modified?
A: Yes. Use WITH (DROP_EXISTING=ON)
:
create table Friend (
UserId int not null,
Id int not null);
go
create clustered index cdxFriend on Friend (UserId, Id);
go
create clustered index cdxFriend on Friend (Id, UserId) with (drop_existing=on);
go
Q2) Can the clustered index of a table that has a primary key constraint be modified?
A: Yes, same as above, as long as the primary key constraint is not enforced via the clustered index:
create table Friend (
UserId int not null,
Id int not null identity(1,1),
constraint pk_Friend primary key nonclustered (Id));
create clustered index cdxFriend on Friend (UserId, Id);
go
create clustered index cdxFriend on Friend (Id, UserId) with (drop_existing=on);
go
Q3) Can the primary key constraint of a table be modified?
A: Yes, as long as the primary constraint is not enforced via the clustered index:
create table Friend (
UserId int not null,
Id int not null identity(1,1),
constraint pk_Friend primary key nonclustered (Id));
go
create clustered index cdxFriend on Friend (UserId, Id);
go
alter table Friend drop constraint pk_Friend;
alter table Friend add constraint pk_Friend primary key nonclustered (UserId)
go
Q4) Can the primary key of a table be modified when is enforced via the clustered index?
A: Yes, if the table never had any rows:
create table Friend (
UserId int not null,
Id int not null identity(1,1),
constraint pk_Friend primary key clustered (UserId, Id));
go
alter table Friend drop constraint pk_Friend;
alter table Friend add constraint pk_Friend primary key clustered (Id, UserId)
go
Q5) Can the primary key of a table be modified when is enforced via the clustered index if the table is populated?
A: No. Any operation that converts a populated clustered index into a heap will be blocked in SQL Azure, even if the table is empty:
create table Friend (
UserId int not null,
Id int not null identity(1,1),
constraint pk_Friend primary key clustered (UserId, Id));
go
insert into Friend (UserId) values (1);
delete from Friend;
go
alter table Friend drop constraint pk_Friend;
As a side note: the constraint can be modified if the table is truncated.
The workaround to change the PK constraint of a populated table is to do the good old sp_rename
trick:
create table Friend (
UserId int not null,
Id int not null identity(1,1),
constraint pk_Friend primary key clustered (UserId, Id));
go
insert into Friend (UserId) values (1);
go
create table FriendNew (
UserId int not null,
Id int not null identity(1,1),
constraint pk_Friend_New primary key clustered (Id, UserId));
go
set identity_insert FriendNew on;
insert into FriendNew (UserId, Id)
select UserId, Id
from Friend;
set identity_insert FriendNew off;
go
begin transaction
exec sp_rename 'Friend', 'FriendOld';
exec sp_rename 'FriendNew', 'Friend';
commit;
go
sp_help 'Friend';
The sp_rename
approach has some issues, most importantly being that permissions on the table do not carry over during the rename, as well as foreign key constraints.
Disclaimer
This is experimental and only tested rudimentarily. Proceed at your own risk. I would not use it myself and just drop / recreate constraints with standard DDL commands. If you break entries in the catalog tables you could easily mess up your database.
For all I know, there are only two differences between a PRIMARY KEY
and a UNIQUE
constraint in the catalog tables (the index itself is identical):
pg_index.indisprimary
:
For PRIMARY KEY constraint ... TRUE
For UNIQUE constraint ... FALSE
pg_constraint.contype
:
PRIMARY KEY constraint ... 'p'
UNIQUE constraint ... 'u'
You could convert constraint and index in place, from PRIMARY KEY
constraint to UNIQUE
constraint, my_idx
being the (optionally schema-qualified) index name:
UPDATE pg_index SET indisprimary = FALSE WHERE indexrelid = 'my_idx'::regclass
UPDATE pg_constraint SET contype = 'u' WHERE conindid = 'my_idx'::regclass;
Or upgrade from UNIQUE
to PRIMARY KEY
:
UPDATE pg_index SET indisprimary = TRUE WHERE indexrelid = 'my_idx'::regclass;
UPDATE pg_constraint SET contype = 'p' WHERE conindid = 'my_idx'::regclass;
Best Answer
Having primary key is absolute necessity for any table.
But obviously before you do that you need to see whether adding a primary key will affect the application itself. If written in ASP.Net it will require model update. Also without primary key entity model looks at it as a view, which is mostly used for displaying data. Adding primary key would cause a conflict and you would have to explicitly create a new view.
If you already have a clustered index on some column, adding a primary key on another column would simply create a primary key constraint. Where as if you do not have a clustered index (heap table), and create a primary key it will implicitly create a clustered index as well on that column.
Probably worst option would be creating a new table and migrating data, simply due to the fact that you would loose all the indexes(if any) were on the table,also if the table had some relationship with the other table you wouldnt be able to delete it. Or if explicitly some one before you added some security rights on the table, would be lost.
If possible try to add primary key on already created table, and existing column.