Wie kann ich einen vorhandenen Primärschlüssel in SQL Azure ändern?

25

Ich möchte einen vorhandenen Primärschlüssel in einer SQL Azure-Tabelle ändern.
Derzeit hat es eine Spalte, und ich möchte eine weitere hinzufügen.

Nun, auf SQL Server 2008 war dies ein Kinderspiel, nur in SSMS, poof. Getan. So sieht die PK aus, wenn ich sie über SQL Server skripte:

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

Wenn ich jedoch in SQL Azure versuche, das Obige auszuführen, schlägt dies natürlich fehl:

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

Gut, also versuche ich den Schlüssel fallen zu lassen:

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

Ok, also versuche ich, einen temporären Clustered-Index zu erstellen, um die PK zu löschen:

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

Was in ... endet: Cannot create more than one clustered index on table 'Friend'. Drop the existing clustered index 'PK_Friend' before creating another.

Großartig, ein guter Moment.

Wie füge ich die UserId-Spalte zu meiner vorhandenen PK hinzu?

Magnus
quelle

Antworten:

34

Hinweis: Ab Azure SQL Database v12 gelten diese Einschränkungen nicht mehr.

Das ist kein Primärindex. Es gibt so etwas wie einen "Primärschlüssel" und es gibt auch so etwas wie einen "Clustered Index". Eindeutige Konzepte, oft verwirrt. In Anbetracht dieser Unterscheidung lassen Sie uns die Frage noch einmal betrachten:

F1) Kann der Clustered-Index in einer SQL Azure-Tabelle geändert werden?
A: Ja. Verwendung 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

F2) Kann der Clustered-Index einer Tabelle mit einer Primärschlüsseleinschränkung geändert werden?
A: Ja, genau wie oben, solange die Primärschlüsseleinschränkung nicht über den Clustered-Index erzwungen wird:

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

F3) Kann die Primärschlüsselbedingung einer Tabelle geändert werden?
A: Ja, solange die primäre Einschränkung nicht über den Clustered-Index erzwungen wird:

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

F4) Kann der Primärschlüssel einer Tabelle geändert werden, wenn er über den Clustered-Index erzwungen wird?
A: Ja, wenn die Tabelle keine Zeilen hatte:

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

F5) Kann der Primärschlüssel einer Tabelle geändert werden, wenn er über den Clustered-Index erzwungen wird, wenn die Tabelle gefüllt ist?
A: Nein. Alle Vorgänge, die einen aufgefüllten Clustered-Index in einen Heap konvertieren, werden in SQL Azure blockiert, auch wenn die Tabelle leer ist :

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;

Als Randnotiz: Die Einschränkung kann geändert werden, wenn die Tabelle abgeschnitten ist .

Die Problemumgehung zum Ändern der PK-Einschränkung einer aufgefüllten Tabelle besteht darin, den guten alten sp_renameTrick auszuführen:

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

Der sp_renameAnsatz weist einige Probleme auf, insbesondere, dass Berechtigungen für die Tabelle beim Umbenennen nicht übertragen werden, sowie Einschränkungen für Fremdschlüssel.

Remus Rusanu
quelle
A1-A4 sind in meinem Fall keine Antworten. A5 hat es geschafft, obwohl meine ID keine Identitätsspalte ist.
Magnus
Die Problemumgehung für sp_rename war hilfreich!
Justin