it-swarm.dev

Jak skopiować tabelę za pomocą SELECT INTO, ale zignorować właściwość TOŻSAMOŚĆ?

Mam tabelę z kolumną tożsamości powiedzieć:

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

Dobrze wiadomo, że to

select * into copy_from_with_id_1 from with_id;

powoduje także copy_from_with_id_1 z tożsamością na id.

Następujące pytanie o przepełnienie stos wspomina o jawnym wyświetleniu wszystkich kolumn.

Spróbujmy

select id, val into copy_from_with_id_2 from with_id;

Ups, nawet w tym przypadku identyfikator jest kolumną tożsamości.

To, czego chcę, to stół

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

Od Books Online

Format nowej_tabeli określa się, oceniając wyrażenia na liście wyboru. Kolumny w nowej_tabeli są tworzone w kolejności określonej przez listę wyboru. Każda kolumna w tabeli nowa_ ma taką samą nazwę, typ danych, dopuszczalność nullab i wartość jak odpowiednie wyrażenie na liście wyboru. Właściwość TOŻSAMOŚĆ kolumny jest przenoszona , z wyjątkiem warunków określonych w „Praca z kolumnami tożsamości” w sekcji Uwagi.

W dół strony:

Po wybraniu istniejącej kolumny tożsamości do nowej tabeli nowa kolumna dziedziczy właściwość TOŻSAMOŚĆ, chyba że spełniony jest jeden z następujących warunków:

  • Instrukcja SELECT zawiera funkcję join, GROUP BY lub funkcję agregującą.
  • Wiele instrukcji SELECT jest łączonych za pomocą UNION.
  • Kolumna tożsamości znajduje się na liście wyboru więcej niż jeden raz.
  • Kolumna tożsamości jest częścią wyrażenia.
  • Kolumna tożsamości pochodzi ze zdalnego źródła danych.

Jeśli którykolwiek z tych warunków jest spełniony, kolumna jest tworzona NIE NULL zamiast dziedziczenia właściwości TOŻSAMOŚĆ. Jeśli w nowej tabeli wymagana jest kolumna tożsamości, ale taka kolumna nie jest dostępna lub chcesz mieć wartość początkową lub przyrostową inną niż kolumna tożsamości źródłowej, zdefiniuj kolumnę na liście wyboru za pomocą funkcji TOŻSAMOŚĆ. Zobacz „Tworzenie kolumny tożsamości za pomocą funkcji TOŻSAMOŚĆ” w sekcji Przykłady poniżej.

Więc ... teoretycznie można uciec:

select id, val 
into copy_from_with_id_2 
from with_id

union all

select 0, 'test_row' 
where 1 = 0;

Ważne byłoby skomentowanie tego kodu, aby go wyjaśnić, aby nie został usunięty następnym razem, gdy ktoś na niego spojrzy.

Zainspirowany odpowiedzią Ericssona znalazłem następujące rozwiązanie, które zależy tylko od nazw tabel i nie używa żadnej konkretnej nazwy kolumny:

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;

Edytuj

Można nawet to poprawić

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

Możesz użyć złączenia, aby utworzyć i wypełnić nową tabelę za jednym razem:

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

Z powodu 1 = 0 warunek, prawa strona nie będzie pasowała, a tym samym zapobiegnie duplikacji rzędów po lewej stronie, a ponieważ jest to połączenie zewnętrzne, rzędy po lewej stronie również nie zostaną wyeliminowane. Wreszcie, ponieważ jest to sprzężenie, właściwość TOŻSAMOŚĆ została wyeliminowana.

Dlatego wybranie tylko lewej kolumny spowoduje utworzenie dokładnej kopii dbo.TableWithIdentity tylko w zakresie danych, tj. Z usuniętą właściwością TOŻSAMOŚĆ.

Wszystko, co zostało powiedziane, Max Vernon podniósł ważny punkt w komentarzu, o którym warto pamiętać. Jeśli spojrzysz na plan wykonania powyższego zapytania:

Execution plan

zauważysz, że tabela źródłowa jest wymieniona w planie wykonania tylko raz. Drugi przypadek został wyeliminowany przez optymalizator.

Jeśli więc optymalizator może poprawnie ustalić, że prawa strona łączenia nie jest potrzebna w planie, uzasadnione jest oczekiwanie, że w przyszłej wersji programu SQL Server może być w stanie stwierdzić, że właściwość TOŻSAMOŚĆ nie musi być usunięto również, ponieważ w źródłowym zestawie wierszy nie ma już innej kolumny TOŻSAMOŚCI zgodnie z planem zapytań. Oznacza to, że powyższe zapytanie może w pewnym momencie przestać działać zgodnie z oczekiwaniami.

Ale, jak słusznie zauważył ypercubeᵀᴹ , do tej pory podręcznik wyraźnie stwierdzał, że jeśli istnieje sprzężenie, właściwość TOŻSAMOŚĆ nie jest zachowywana:

Gdy istniejąca kolumna tożsamości zostanie wybrana do nowej tabeli, nowa kolumna odziedziczy właściwość TOŻSAMOŚĆ, chyba że [...] [t] on instrukcja SELECT zawiera sprzężenie.

Tak więc, o ile instrukcja ciągle o tym wspomina, możemy być pewni, że zachowanie pozostanie takie samo.

Uznanie dla Shaneis i ypercubeᵀᴹ za poruszenie pokrewnego tematu na czacie.

13
Andriy M

Wypróbuj ten kod ..

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

Wywołanie ISNULL zapewnia, że ​​nowa kolumna zostanie utworzona z NOT NULL nullability.

6
Saurav Ghosh

Aby pokazać inny sposób:

Możesz użyć połączonego serwera.

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

Możesz tymczasowo utworzyć serwer połączony z serwerem lokalnym, używając:

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;

W tym momencie uruchomiłbyś select * into kod, odwołując się do localserver połączonego czteroczęściowego serwera:

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

Po zakończeniu wyczyść połączony serwer localserver za pomocą:

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

Lub możesz użyć składni OPENQUERY

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

Właściwość tożsamości nie jest przenoszona, jeśli instrukcja select zawiera złączenie i tak dalej

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

da również pożądane zachowanie (skopiowanej kolumny id, aby nie zachowywać właściwości IDENTITY. Jednak efektem ubocznym będzie nie kopiowanie żadnego wiersza! (jak w przypadku niektórych innych metody), więc musisz wykonać:

insert into without_id select * from with_id;

(dzięki AakashM!)

1
anon-99

Najprostszym sposobem jest uczynienie kolumny częścią wyrażenia.

Przykład:
Jeśli tabela dbo. Pracownik ma tożsamość w kolumnie z identyfikatorem, to w poniższym przykładzie tabela tymczasowa #t będzie miała również TOŻSAMOŚĆ w kolumnie z identyfikatorem.

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

Zmień to, aby zastosować wyrażenie do identyfikatora, a Ty nie będziesz już mieć TOŻSAMOŚCI w kolumnie Identyfikator. W takim przypadku zastosujemy prosty dodatek do kolumny identyfikatora.

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

Inne przykłady wyrażeń dla innych typów danych mogą obejmować: konwersja (), łączenie łańcuchów lub Isnull ()

1
FistOfFury

Czasami chcesz wstawić z tabeli, w której nie wiesz (lub nie obchodzi Cię), czy kolumna została utworzona przy użyciu TOŻSAMOŚCI, czy nie. Może nie być nawet kolumną z liczbą całkowitą, z którą pracujesz. W takim przypadku będą działać następujące funkcje:

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

ISNULL usunie atrybut TOŻSAMOŚĆ z kolumny, ale wstawi go o tej samej nazwie i typie co oryginalna kolumna, a także sprawi, że nie będzie on miał wartości zerowej. TOP (0) utworzy pustą tabelę, w której możesz następnie wstawić wybrane wiersze. W razie potrzeby tabelę można również skompresować przed wstawieniem danych.

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

usunie tożsamość.

Minusem jest to, że id staje się zerowalne, ale można dodać to ograniczenie.

0
john hunter