Как я могу изменить существующий первичный ключ в SQL Azure?

25

Я хочу изменить существующий первичный ключ в таблице SQL Azure.
В настоящее время он имеет один столбец, и я хочу добавить другой.

Теперь на SQL Server 2008 это было просто, просто сделал это в SSMS, пуф. Выполнено. Вот как выглядит PK, если я пишу его с SQL Server:

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

Тем не менее, в SQL Azure, когда я пытаюсь выполнить вышеизложенное, это, конечно же, завершится ошибкой

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

Хорошо, поэтому я пытаюсь уронить ключ:

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

Итак, я пытаюсь создать временный кластерный индекс для удаления PK:

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

Что приводит к: Cannot create more than one clustered index on table 'Friend'. Drop the existing clustered index 'PK_Friend' before creating another.

Отлично, момент уловки22.

Как добавить столбец UserId в мою существующую PK?

Магнус
источник
Связанный: stackoverflow.com/questions/5645145/…
Ник Чаммас

Ответы:

34

Примечание. Начиная с базы данных SQL Azure v12, эти ограничения больше не применяются.

Нет такого понятия, как «первичный индекс». Существует такая вещь, как «первичный ключ», а также такая вещь, как «кластерный индекс». Разные понятия, часто путают. Имея это в виду, давайте вернемся к вопросу:

В1) Можно ли изменить кластерный индекс в таблице SQL Azure?
A: Да. Используйте 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) Можно ли изменить кластерный индекс таблицы с ограничением первичного ключа?
A: Да, так же, как и выше, до тех пор, пока ограничение первичного ключа не применяется через кластеризованный индекс:

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

В3) Можно ли изменить ограничение первичного ключа таблицы?
A: Да, если основное ограничение не применяется через кластеризованный индекс:

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

В4) Можно ли изменить первичный ключ таблицы, если он применяется через кластерный индекс?
A: Да, если в таблице никогда не было строк:

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

В5) Можно ли изменить первичный ключ таблицы, если он применяется через кластерный индекс, если таблица заполнена?
A: Нет. Любая операция, которая преобразует заполненный кластерный индекс в кучу, будет заблокирована в SQL Azure, даже если таблица пуста :

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;

В качестве примечания: ограничение может быть изменено, если таблица усекается .

Обходной путь для изменения ограничения PK заполненной таблицы заключается в том, чтобы сделать старый добрый sp_renameтрюк:

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

sp_renameПодход имеет некоторые проблемы, самое главное в том , что разрешения на столе не переносятся во время переименования, а также иностранных ключевых ограничений.

Ремус Русану
источник
А1-А4 Нет ответов в моем случае. A5 добился цели, хотя мой идентификатор не является столбцом идентификации.
Магнус
Обходной путь sp_rename оказался полезным!
Джастин