Comment puis-je modifier une clé primaire existante sur SQL Azure?

25

Je souhaite modifier une clé primaire existante sur une table SQL Azure.
Il a actuellement une colonne, et je veux en ajouter une autre.

Maintenant, sur SQL Server 2008, c'était un morceau de gâteau, je viens de le faire dans SSMS, pouf. Terminé. Voici à quoi ressemble le PK si je le script à partir de SQL Server:

ALTER TABLE [dbo].[Friend] ADD  CONSTRAINT [PK_Friend] PRIMARY KEY CLUSTERED 
(
  [UserId] ASC,
  [Id] ASC
)

Cependant, sur SQL Azure, lorsque j'essaie d'exécuter ce qui précède, cela échouera bien sûr:

Table 'Friend' already has a primary key defined on it.

Très bien, alors j'essaie de laisser tomber la clé:

Tables without a clustered index are not supported in this version of SQL Server. Please create a clustered index and try again.

Ok, j'essaie donc de créer un index cluster temporaire afin de supprimer le PK:

CREATE CLUSTERED INDEX IX_Test ON [Friend] ([UserId],[Id])

Ce qui se traduit par: Cannot create more than one clustered index on table 'Friend'. Drop the existing clustered index 'PK_Friend' before creating another.

Super, un moment catch22.

Comment ajouter la colonne UserId à mon PK existant?

Magnus
la source

Réponses:

34

Remarque: depuis Azure SQL Database v12, ces restrictions ne s'appliquent plus.

Il n'y a rien de tel qu'un «indice primaire». Il existe une «clé primaire» et un «index clusterisé». Concepts distincts, souvent confus. Avec cette distinction à l'esprit, revenons à la question:

Q1) L'index cluster dans une table SQL Azure peut-il être modifié?
R: Oui. Utilisation 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) L'index cluster d'une table qui a une contrainte de clé primaire peut-il être modifié?
R: Oui, comme ci-dessus, tant que la contrainte de clé primaire n'est pas appliquée via l'index clusterisé:

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) La contrainte de clé primaire d'une table peut-elle être modifiée?
R: Oui, tant que la contrainte principale n'est pas appliquée via l'index clusterisé:

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) La clé primaire d'une table peut-elle être modifiée lorsqu'elle est appliquée via l'index clusterisé?
R: Oui, si la table n'a jamais eu de lignes:

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) La clé primaire d'une table peut-elle être modifiée lorsqu'elle est appliquée via l'index clusterisé si la table est remplie?
R: Non. Toute opération qui convertit un index cluster rempli en un tas sera bloquée dans SQL Azure, même si la table est vide :

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;

En remarque: la contrainte peut être modifiée si le tableau est tronqué .

La solution de contournement pour modifier la contrainte PK d'une table remplie est de faire le bon vieux sp_renametruc:

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';

L' sp_renameapproche présente certains problèmes, le plus important étant que les autorisations sur la table ne sont pas transférées pendant le renommage, ainsi que les contraintes de clé étrangère.

Remus Rusanu
la source
A1-A4 sont Aucune réponse dans mon cas. A5 a fait l'affaire, bien que mon identifiant ne soit pas une colonne d'identité.
Magnus
La solution de contournement de sp_rename a été utile!
Justin