Видаліть ідентичність зі стовпця в таблиці


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
ви можете видалити специфікацію особи Насправді мені довелося це зробити вчора за допомогою SSMS, хоча не на 500 мільйонів рядків.
Саймон

33
@simon, якщо ви скриптуєте свої зміни, ви побачите, що SSMS фактично створює копію таблиці без властивості ідентичності.
Код Мага

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

1
Людині, яка проголосила, я вдячний почути те, що вам не сподобалось у моїй відповіді.
Адам Венгер

1
Подивіться на відповідь Марка Совула нижче. Уникає необхідності переміщення даних із стовпця в стовпчик. Використовуючи відповідь Марка, ви лише перетасовуєте метадані. Нічого страшного, якщо ви не працюєте над столом, який містить десятки чи сотні мільйонів рядків. Плюс відповідь Марка запобігає переміщенню стовпця в схему таблиці. Я просто спробував це, і це спрацювало як шарм. Дуже розумний.
Ендрю Штейц

100

Якщо ви хочете зробити це без додавання та заповнення нового стовпця , без впорядкування стовпців та майже без простоїв, оскільки жодні дані не змінюються на таблиці, давайте зробимо магію з функціоналом розділення (але оскільки жодні розділи не використовуються, ви не використовуєте не потрібно Enterprise Edition):

  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

11
Це має бути прийнятою відповіддю. Це єдиний реальний спосіб видалити стовпчик ідентичності без масивної міграції даних.
Ваккано

Оце Так! Це дуже розумно.
Ендрю Штайц

Ця відповідь феноменальна! Дякую!
Джон

5
Якщо ви використовуєте SQL Management Studio для скриптування таблиці, не забудьте увімкнути Інструменти> Параметри> Провідник об’єктів SQL Server> Сценарій> Параметри таблиці та параметри перегляду> Індекси сценаріїв (помилково за замовчуванням)
користувач423430

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, скиньте цей стовпець, перейменуйте свій часовий стовпець і додайте до цього стовпця обмеження ПК:

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, я спробував на azure sql-сервері, не всі операції тут підтримуються у azure версії sql.
дасони

Дякую за відповідь! Мені дуже допомогли! Вам просто слід додати перевірку для індексів, на які посилається стовпець. Щось на кшталт: 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-му кроці, він додається наприкінці, але, як правило, ми хочемо, щоб наш стовпчик основного ключа, тобто Id, був перший стовпець у цій таблиці. То чи існує якесь рішення щодо цього?
Ашиш Шукла

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
ЛЮБИТЬ YAAAAAAAAAA. Саме те, що я шукав, простий спосіб уникнути генерації "ІДЕНТИЧНОСТІ" з "SELECT * INTO" (створити нову температуру таблиці для резервних копій, які потрібні)
KurzedMetal

це маленьке і краса;)
Zolfaghari

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

і хочете вставити рядок з ідентифікатором = 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 ви можете увімкнути та вимкнути вставку ідентифікації таким чином:

ВІДКЛЮЧИТИ Ідентифікатор_INSERT ім'я таблиці

- запустіть свої запити тут

ВИМКНЕННЯ Ідентифікатора_INSERT_ ім'я таблиці

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.