Удалить удостоверение из столбца в таблице


127

У нас есть таблица размером 5 ГБ (почти 500 миллионов строк), и мы хотим удалить свойство идентификации в одном из столбцов, но когда мы пытаемся сделать это через SSMS, время истекает.

Можно ли это сделать через T-SQL?


1
Вы можете разместить здесь схему таблицы?
Al W

Я уверен, что есть веские причины, по которым SQL Server не поддерживает удаление свойства идентификации из столбца с помощью простого оператора ALTER TABLE ..., но, тем не менее, в настоящее время мне грустно, что это так.
Джон Шнайдер

Ответы:


143

Вы не можете удалить IDENTITYоднажды установленную спецификацию.

Чтобы удалить весь столбец:

ALTER TABLE yourTable
DROP COLUMN yourCOlumn;

Информация об ALTER TABLE здесь

Если вам нужно сохранить данные, но удалить IDENTITYстолбец, вам необходимо:

  • Создать новый столбец
  • Перенести данные из существующего IDENTITYстолбца в новый столбец
  • Отбросьте существующий IDENTITYстолбец.
  • Переименуйте новый столбец в исходное имя столбца

3
вы можете удалить спецификацию Identity. На самом деле мне пришлось сделать это вчера, используя SSMS, но не на 500 миллионах строк.
Саймон

34
@simon, если вы запишете свои изменения в сценарий, вы увидите, что SSMS фактически создает копию таблицы без свойства идентификации.
Code Magician

3
Я просто хочу добавить, чтобы переименовать новый столбец в имя исходного столбца. Кроме того, если этот identityстолбец используется как часть foreign keyв другой таблице, вам придется сначала сбросить ограничения, а затем принять меры, поскольку @AdamWenger упомянул об удалении идентификатора. attribute/propertyВы также можете просмотреть эту ссылку для получения более подробной информации только об удалении атрибута: blog.sqlauthority.com/2009/05/03/… .. Удачи!
Nonym

1
Человеку, проголосовавшему против - я был бы признателен, если бы вам не понравилось то, что вам не понравилось в моем ответе.
Адам Венгер,

1
Взгляните на ответ Марка Соула ниже. Устраняет необходимость перемещать данные из столбца в столбец. Используя ответ Марка, вы только перетасовываете метаданные. Ничего страшного, если вы не работаете с таблицей, содержащей десятки или сотни миллионов строк. Плюс ответ Марка предотвращает перемещение столбца в схеме таблицы. Я просто попробовал, и это сработало как шарм. Очень умный.
Эндрю Стейтц

100

Если вы хотите сделать это без добавления и заполнения нового столбца , без переупорядочивания столбцов и почти без простоев из-за того, что данные в таблице не меняются, давайте сделаем магию с функцией разделения (но поскольку разделы не используются, вы не не нужна версия Enterprise):

  1. Удалите все внешние ключи, указывающие на эту таблицу
  2. Создайте скрипт для создаваемой таблицы; переименуйте все, например, «MyTable2», «MyIndex2» и т. д. Удалите спецификацию IDENTITY.
  3. Теперь у вас должно быть две «одинаковых» таблицы, одна полная, а другая пустая без ИДЕНТИФИКАЦИИ.
  4. Бегать ALTER TABLE [Original] SWITCH TO [Original2]
  5. Теперь ваша исходная таблица будет пустой, а в новой будут данные. Вы переключили метаданные для двух таблиц (мгновенно).
  6. Отбросьте исходную (теперь пустую таблицу), exec sys.sp_renameчтобы переименовать различные объекты схемы обратно в исходные имена, а затем вы можете воссоздать свои внешние ключи.

Например, учитывая:

CREATE TABLE Original
(
  Id INT IDENTITY PRIMARY KEY
, Value NVARCHAR(300)
);
CREATE NONCLUSTERED INDEX IX_Original_Value ON Original (Value);

INSERT INTO Original
SELECT 'abcd'
UNION ALL 
SELECT 'defg';

Вы можете сделать следующее:

--create new table with no IDENTITY
CREATE TABLE Original2
(
  Id INT PRIMARY KEY
, Value NVARCHAR(300)
);
CREATE NONCLUSTERED INDEX IX_Original_Value2 ON Original2 (Value);

--data before switch
SELECT 'Original', *
FROM Original
UNION ALL
SELECT 'Original2', *
FROM Original2;

ALTER TABLE Original SWITCH TO Original2;

--data after switch
SELECT 'Original', *
FROM Original
UNION ALL
SELECT 'Original2', *
FROM Original2;

--clean up 
IF NOT EXISTS (SELECT * FROM Original) DROP TABLE Original;
EXEC sys.sp_rename 'Original2.IX_Original_Value2', 'IX_Original_Value', 'INDEX';
EXEC sys.sp_rename 'Original2', 'Original', 'OBJECT';


UPDATE Original
SET Id = Id + 1;

SELECT *
FROM Original;

У меня нет времени на самом деле попробовать это, но это хорошо знать, когда в следующий раз мне действительно понадобится отказаться от личности.
CoderDennis,

12
Это должен быть принятый ответ. Это единственный реальный способ удалить столбец идентификаторов без массового переноса данных.
Vaccano,

Вот Это Да! Это очень умно.
Эндрю Стейтц

Это феноменальный ответ! Спасибо!
Джон

5
При использовании SQL Management Studio для создания сценария таблицы обязательно включите Инструменты> Параметры> Обозреватель объектов SQL Server> Сценарии> Параметры таблицы и просмотра> Индексы сценариев (по умолчанию
false

61

Это создает путаницу с ограничениями внешнего и первичного ключей, поэтому вот несколько сценариев, которые помогут вам на вашем пути:

Сначала создайте повторяющийся столбец с временным именем:

alter table yourTable add tempId int NOT NULL default -1;
update yourTable set tempId = id;

Затем получите имя ограничения первичного ключа:

SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'yourTable';

Теперь попробуйте сбросить ограничение первичного ключа для своего столбца:

ALTER TABLE yourTable DROP CONSTRAINT PK_yourTable_id;

Если у вас есть внешние ключи, это не удастся, поэтому в таком случае следует отказаться от ограничений внешнего ключа. СОХРАНИТЕ СЛЕДУЮЩИЕ ТАБЛИЦЫ, КОТОРЫЕ ВЫ ЗАПУСКАЕТЕ, ЧТОБЫ ДОБАВИТЬ ОГРАНИЧЕНИЯ НАЗАД В ПОЗЖЕ !!!

SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'otherTable';
alter table otherTable drop constraint fk_otherTable_yourTable;
commit;
..

После того, как все ваши ограничения внешнего ключа будут удалены, вы сможете удалить ограничение PK, отбросить этот столбец, переименовать свой временный столбец и добавить ограничение PK в этот столбец:

ALTER TABLE yourTable DROP CONSTRAINT PK_yourTable_id;
alter table yourTable drop column id;
EXEC sp_rename 'yourTable.tempId', 'id', 'COLUMN';
ALTER TABLE yourTable ADD CONSTRAINT PK_yourTable_id PRIMARY KEY (id) 
commit;

Наконец, снова добавьте ограничения FK:

alter table otherTable add constraint fk_otherTable_yourTable foreign key (yourTable_id) references yourTable(id);
..

Эль Фин!


Обратите внимание на версию вашего сервера sql, я пробовал на сервере sql azure, не все операции здесь поддерживаются в версии sql azure.
dasons

Спасибо за ответ! Мне очень помогли! Вам просто нужно добавить проверку индексов, которые ссылаются на столбец. Что-то вроде: Select t.name 'Table', i.name 'Index', c.name 'Column', i.is_primary_key, i.is_unique From sys.tables t Inner Join sys.indexes i On i.object_id = t.object_id Inner Join sys.index_columns ic ON ic.object_id = i.object_id And i.index_id = ic.index_id Inner Join sys.columns c ON ic.object_id = c.object_id and ic.column_id = c.column_id Where t.name = 'tableName' Order By t.name, i.name, i.index_id, ic.index_column_id
Александр Юнгес

хороший трюк, мне очень помог, но один момент, который я хочу упомянуть здесь, это то, что когда мы добавляем новый повторяющийся столбец, как упомянуто в 1-м шаге, он добавляется в конце, но обычно мы хотим, чтобы наш столбец первичного ключа, т.е. 1-й столбец в этой таблице. Так есть ли обходной путь?
Ашиш Шукла

19

У меня была такая же проблема. 4 оператора в SSMS вместо использования графического интерфейса, и это было очень быстро.

  • Сделайте новую колонку

    alter table users add newusernum int;

  • Копировать значения поверх

    update users set newusernum=usernum;

  • Отбросьте старую колонку

    alter table users drop column usernum;

  • Переименуйте новый столбец в старое имя столбца

    EXEC sp_RENAME 'users.newusernum' , 'usernum', 'COLUMN';


Это не так просто, если у вас есть ограничения на старый столбец.
Suncat2000

13

Следующий скрипт удаляет поле Identity для столбца с именем Id.

Надеюсь, поможет.

BEGIN TRAN
BEGIN TRY
    EXEC sp_rename '[SomeTable].[Id]', 'OldId';

    ALTER TABLE [SomeTable] ADD Id int NULL

    EXEC ('UPDATE [SomeTable] SET Id = OldId')

    ALTER TABLE [SomeTable] NOCHECK CONSTRAINT ALL

    ALTER TABLE [SomeTable] DROP CONSTRAINT [PK_constraintName];
    ALTER TABLE [SomeTable] DROP COLUMN OldId
    ALTER TABLE [SomeTable] ALTER COLUMN [Id] INTEGER NOT NULL
    ALTER TABLE [SomeTable] ADD CONSTRAINT PK_JobInfo PRIMARY KEY (Id)

    ALTER TABLE [SomeTable] CHECK CONSTRAINT ALL

    COMMIT TRAN
END TRY
BEGIN CATCH
    ROLLBACK TRAN   
    SELECT ERROR_MESSAGE ()
END CATCH

4

Нижний код работает нормально, когда мы не знаем имя столбца идентификатора .

Необходимо скопировать данные в новую временную таблицу, например Invoice_DELETED. и в следующий раз мы используем:

insert into Invoice_DELETED select * from Invoice where ...


SELECT t1.*
INTO Invoice_DELETED
FROM Invoice t1
LEFT JOIN Invoice ON 1 = 0
--WHERE t1.InvoiceID = @InvoiceID

Для получения дополнительной информации см. Https://dba.stackexchange.com/a/138345/101038.


1
ЛЮБОВЬ ЯААААААААА. Именно то, что я искал, простой способ избежать генерации «IDENTITY» из «SELECT * INTO» (для создания новой таблицы temp для необходимых резервных копий)
KurzedMetal

он маленький и красивый;)
Золфагари 07

3
ALTER TABLE tablename add newcolumn int
update tablename set newcolumn=existingcolumnname
ALTER TABLE tablename DROP COLUMN existingcolumnname;
EXEC sp_RENAME 'tablename.oldcolumn' , 'newcolumnname', 'COLUMN'

Однако приведенный выше код работает, только если нет отношения первичного и внешнего ключа.


1

Просто для тех, у кого такая же проблема, как и у меня. Если вы просто хотите сделать вставку только один раз, вы можете сделать что-то вроде этого.

Предположим, у вас есть таблица с двумя столбцами

ID Identity (1,1) | Name Varchar

и хотите вставить строку с ID = 4. Итак, вы переустановили ее на 3, поэтому следующая - 4

DBCC CHECKIDENT([YourTable], RESEED, 3)

Сделайте вставку

INSERT  INTO [YourTable]
        ( Name )
VALUES  ( 'Client' )

И верните свое семя к наивысшему идентификатору, допустим, 15

DBCC CHECKIDENT([YourTable], RESEED, 15)

Готово!


1

У меня было такое же требование, и вы могли попробовать этот способ, который я лично вам рекомендую. Пожалуйста, вручную спроектируйте свою таблицу и сгенерируйте скрипт, и то, что я сделал ниже, было переименованием старой таблицы, а также ее ограничением для резервного копирования.

/* To prevent any potential data loss issues, you should review this script in detail before running it outside the context of the database designer.*/
BEGIN TRANSACTION

SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
ALTER TABLE dbo.SI_Provider_Profile
    DROP CONSTRAINT DF_SI_Provider_Profile_SIdtDateTimeStamp
GO
ALTER TABLE dbo.SI_Provider_Profile
    DROP CONSTRAINT DF_SI_Provider_Profile_SIbHotelPreLoaded
GO
CREATE TABLE dbo.Tmp_SI_Provider_Profile
    (
    SI_lProvider_Profile_ID int NOT NULL,
    SI_lSerko_Integrator_Token_ID int NOT NULL,
    SI_sSerko_Integrator_Provider varchar(50) NOT NULL,
    SI_sSerko_Integrator_Profile varchar(50) NOT NULL,
    SI_dtDate_Time_Stamp datetime NOT NULL,
    SI_lProvider_ID int NULL,
    SI_sDisplay_Name varchar(10) NULL,
    SI_lPurchased_From int NULL,
    SI_sProvider_UniqueID varchar(255) NULL,
    SI_bHotel_Pre_Loaded bit NOT NULL,
    SI_sSiteName varchar(255) NULL
    )  ON [PRIMARY]
GO
ALTER TABLE dbo.Tmp_SI_Provider_Profile SET (LOCK_ESCALATION = TABLE)
GO
ALTER TABLE dbo.Tmp_SI_Provider_Profile ADD CONSTRAINT
    DF_SI_Provider_Profile_SIdtDateTimeStamp DEFAULT (getdate()) FOR SI_dtDate_Time_Stamp
GO
ALTER TABLE dbo.Tmp_SI_Provider_Profile ADD CONSTRAINT
    DF_SI_Provider_Profile_SIbHotelPreLoaded DEFAULT ((0)) FOR SI_bHotel_Pre_Loaded
GO
IF EXISTS(SELECT * FROM dbo.SI_Provider_Profile)
        EXEC('INSERT INTO dbo.Tmp_SI_Provider_Profile (SI_lProvider_Profile_ID, SI_lSerko_Integrator_Token_ID, SI_sSerko_Integrator_Provider, SI_sSerko_Integrator_Profile, SI_dtDate_Time_Stamp, SI_lProvider_ID, SI_sDisplay_Name, SI_lPurchased_From, SI_sProvider_UniqueID, SI_bHotel_Pre_Loaded, SI_sSiteName)
        SELECT SI_lProvider_Profile_ID, SI_lSerko_Integrator_Token_ID, SI_sSerko_Integrator_Provider, SI_sSerko_Integrator_Profile, SI_dtDate_Time_Stamp, SI_lProvider_ID, SI_sDisplay_Name, SI_lPurchased_From, SI_sProvider_UniqueID, SI_bHotel_Pre_Loaded, SI_sSiteName FROM dbo.SI_Provider_Profile WITH (HOLDLOCK TABLOCKX)')
GO

-- Rename the primary key constraint or unique key In SQL Server constraints such as primary keys or foreign keys are objects in their own right, even though they are dependent upon the "containing" table.
EXEC sp_rename 'dbo.SI_Provider_Profile.PK_SI_Provider_Profile', 'PK_SI_Provider_Profile_Old';
GO
-- backup old table in case of 
EXECUTE sp_rename N'dbo.SI_Provider_Profile', N'SI_Provider_Profile_Old', 'OBJECT'
GO

EXECUTE sp_rename N'dbo.Tmp_SI_Provider_Profile', N'SI_Provider_Profile', 'OBJECT'
GO

ALTER TABLE dbo.SI_Provider_Profile ADD CONSTRAINT
    PK_SI_Provider_Profile PRIMARY KEY NONCLUSTERED 
    (
    SI_lProvider_Profile_ID
    ) WITH( PAD_INDEX = OFF, FILLFACTOR = 90, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO
COMMIT TRANSACTION

1

В SQL Server вы можете включать и выключать вставку удостоверения следующим образом:

УСТАНОВИТЬ IDENTITY_INSERT имя_таблицы ВКЛ.

- запустите свои запросы здесь

ВЫКЛЮЧИТЬ IDENTITY_INSERT имя_таблицы

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.