Як скопіювати таблицю SELECT INTO, але ігнорувати властивість IDENTITY?


41

У мене таблиця зі стовпцем особи говорить:

create table with_id (
 id int identity(1,1),
 val varchar(30)
);

Добре відомо, що це

select * into copy_from_with_id_1 from with_id;

призводить до copy_from_with_id_1 з ідентифікацією на id.

У наступному питанні про переповнення стека згадується чіткий перелік усіх стовпців.

Спробуймо

select id, val into copy_from_with_id_2 from with_id;

На жаль, навіть у цьому випадку id - стовпець ідентичності.

Те, що я хочу, це таблиця, як

create table without_id (
 id int,
 val varchar(30)
);

Відповіді:


52

З книг Інтернет

Формат new_table визначається шляхом оцінки виразів у списку select. Стовпці в new_table створюються в порядку, визначеному списком select. Кожен стовпець у new_table має те саме ім’я, тип даних, зведення нанівець та значення, як і відповідний вираз у списку вибору. Властивість ідентичності стовпця передається, за винятком умов, визначених у розділі "Робота зі стовпцями особи " в розділі "Зауваження".

Внизу сторінки:

Коли в новій таблиці вибрано існуючий стовпець ідентичності, новий стовпець успадковує властивість IDENTITY, якщо не виконується одне з наступних умов:

  • Оператор SELECT містить приєднання, пункт GROUP BY або функцію сукупності.
  • Кілька операторів SELECT з'єднуються за допомогою UNION.
  • Стовпець посвідчення вноситься не один раз у список вибору.
  • Стовпець ідентичності є частиною виразу.
  • Стовпець ідентичності відбувається з віддаленого джерела даних.

Якщо будь-яке з цих умов є істинним, стовпець створюється НЕ NULL замість успадкування властивості IDENTITY. Якщо стовпець ідентичності потрібен у новій таблиці, але такий стовпець недоступний, або ви хочете значення початкового чи приросту, що відрізняється від стовпця ідентичності джерела, визначте стовпець у списку вибору за допомогою функції ІДЕНТИЧНОСТІ. Див. "Створення стовпця ідентичності за допомогою функції ІДЕНТИЧНОСТІ" в розділі "Приклади" нижче.

Отже ... теоретично ви могли б піти з:

select id, val 
into copy_from_with_id_2 
from with_id

union all

select 0, 'test_row' 
where 1 = 0;

Було б важливо прокоментувати цей код, щоб пояснити його, щоб він не був видалений наступного разу, коли хтось його погляне.


29

Натхненний відповіддю Erics, я знайшов таке рішення, яке залежить лише від назв таблиці та не використовує жодної конкретної назви стовпця:

select * into without_id from with_id where 1 = 0
union all
select * from with_id where 1 = 0
;
insert into without_id select * from with_id;

Редагувати

Можна навіть покращити це до

select * into without_id from with_id
union all
select * from with_id where 1 = 0
;

13

Ви можете використовувати приєднання, щоб створити та заповнити нову таблицю за один раз:

SELECT
  t.*
INTO
  dbo.NewTable
FROM
  dbo.TableWithIdentity AS t
  LEFT JOIN dbo.TableWithIdentity ON 1 = 0
;

Зважаючи на 1 = 0умову, правий бік не матиме збігів і, таким чином, запобігає дублюванню лівих бічних рядків, а оскільки це зовнішнє з'єднання, ліві бічні рядки також не будуть усунені. Нарешті, оскільки це об'єднання, властивість IDENTITY усувається.

Таким чином, вибравши лише ліві стовпчики зліва, вийде точна копія dbo.TableWithIdentity лише для даних, тобто з відключеним властивістю IDENTITY.

Маючи на увазі , Макс Вернон підкреслив вагомий пункт у коментарі, який варто пам’ятати. Якщо ви подивитесь на план виконання вищезазначеного запиту:

План виконання

ви помітите, що джерельна таблиця згадується в плані виконання лише один раз. Інший екземпляр був усунений оптимізатором.

Отже, якщо оптимізатор може правильно встановити, що права частина з'єднання не потрібна в плані, слід розумно розраховувати, що в майбутній версії SQL Server він може виявити, що властивість IDENTITY не повинна бути видалено, оскільки у наборі рядків вихідного рядка відповідно до плану запитів більше немає стовпця ІДЕНТИЧНОСТІ. Це означає, що вищезазначений запит може припинити роботу, як очікувалося,

Але, як правильно зазначає ypercubeᵀᴹ , поки що в посібнику явно було зазначено, що якщо є з'єднання, властивість IDENTITY не зберігається:

Коли в новій таблиці вибрано існуючий стовпець ідентичності, новий стовпець успадковує властивість IDENTITY, якщо [...] [t] він SELECT не містить з'єднання.

Отже, доки посібник не згадає про нього, ми можемо бути впевнені, що поведінка залишиться такою ж.

Кудос до Шанеї та йперкубеᵀᴹ для підняття відповідної теми в чаті.


Було б JOIN (SELECT 1) AS dummy ON 1 = 1теж працювати?
ypercubeᵀᴹ


5

Спробуйте цей код ..

SELECT isnull(Tablename_old.IDENTITYCOL + 0, -1) AS 'New Identity Column'
INTO   dbo.TableName_new
FROM   dbo.TableName_old 

ISNULLВиклик гарантує , що новий стовпець створюється з NOT NULLдопустимостью порожнім.


1
Це ISNULL()чи те, +0що це робить? Або обоє потрібні?
ypercubeᵀᴹ

3

Просто показати інший спосіб:

Ви можете використовувати пов'язаний сервер .

SELECT * 
INTO without_id 
FROM [linked_server].[source_db].dbo.[with_id];

Ви можете тимчасово створити зв'язаний сервер з локальним сервером за допомогою цього:

DECLARE @LocalServer SYSNAME 
SET @LocalServer = @@SERVERNAME;
EXEC master.dbo.sp_addlinkedserver @server = N'localserver'
    , @srvproduct = ''
    , @provider = 'SQLNCLI'
    , @datasrc = @LocalServer;
EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname=N'localserver'
    , @useself = N'True'
    , @locallogin = NULL
    , @rmtuser = NULL
    , @rmtpassword = NULL;

У цей момент ви запустите select * intoкод, посилаючись на localserverпов'язаний сервер з чотирма частинами імені:

SELECT * 
INTO without_id 
FROM [localserver].[source_db].dbo.[with_id];

Після цього завершіть очищення localserverзв'язаного сервера з цим:

EXEC sp_dropserver @server = 'localserver'
    , @droplogins = 'droplogins';

Або ви можете використовувати OPENQUERYсинтаксис

SELECT * 
INTO without_id 
FROM OPENQUERY([linked_server], 'SELECT * FROM [source_db].dbo.[with_id]');

1

Властивість ідентичності не передається, якщо оператор select містить з'єднання тощо

select a.* into without_id from with_id a inner join with_id b on 1 = 0;

також надасть потрібну поведінку (скопійованого idстовпця, щоб не зберігати IDENTITYвластивість. Однак, це матиме побічний ефект взагалі не копіювати жоден рядок! (як це стосується деяких інших методів), тому вам потрібно буде зробити:

insert into without_id select * from with_id;

(спасибі AakashM!)


1

Найпростіший спосіб - зробити стовпець частиною виразу.

Приклад:
Якщо таблиця dbo.E Employee має ідентифікатор у стовпці ідентифікатора, то в прикладі, наведеному нижче, таблиця temp #t матиме ідентифікатор у стовпці ID.

--temp table has IDENTITY
select ID, Name 
into #t
from dbo.Employee

Змініть це, щоб застосувати вираз до ідентифікатора, і ви #t більше не матимете Ідентифікатор у стовпці ID. У цьому випадку ми застосовуємо просте доповнення до стовпця з ідентифікатором.

--no IDENTITY
select ID = ID + 0, Name 
into #t
from dbo.Employee

Інші приклади виразів для інших типів даних можуть включати: convert (), конкатенація рядків або Isnull ()


1
З docs.microsoft.com/en-us/sql/t-sql/queries/… : "Коли існуючий стовпець ідентичності вибирається в нову таблицю, новий стовпець успадковує властивість IDENTITY, якщо тільки не виконується одна з наведених нижче умов. … Стовпець ідентичності є частиною виразу… стовпець створюється НЕ НУЛЬОВЕ замість успадкування властивості IDENTITY. »
Мангго,

1

Іноді ви хочете вставити з таблиці, де ви не знаєте (чи не піклуєтесь), чи був стовпець створений з використанням IDENTITY чи ні. Це може бути навіть не цілий стовпець, з яким ви працюєте. У цьому випадку спрацює наступне:

SELECT TOP(0) ISNULL([col],NULL) AS [col], ... INTO [table2] FROM [table1]
ALTER TABLE [table2] REBUILD WITH (DATA_COMPRESSION=page)
INSERT INTO [table2] ...

ISNULL видалить атрибут IDENTITY із стовпця, але вставить його з тим же ім'ям та типом, що і вихідний стовпець, а також зробить його не нульовим. TOP (0) створить порожню таблицю, яку ви можете використовувати для вставки вибраних рядків у. Ви також можете зробити таблицю стиснутою перед тим, як потрібно, щоб вставити дані.


0
select convert(int, id) as id, val 
into copy_from_with_id_without_id 
from with_id;

видалить особистість.

Мінус у тому, що idстає нульовим, але ви можете додати це обмеження.


1
Ви можете використовувати ISNULL, щоб обійти це.
Ерік Дарлінг

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