J'ai une table avec une colonne d'identité qui dit:
create table with_id (
id int identity(1,1),
val varchar(30)
);
Il est bien connu que ce
select * into copy_from_with_id_1 from with_id;
se traduit par copy_from_with_id_1 avec identité sur id aussi.
Ce qui suit question de débordement de pile mentionne la liste explicite de toutes les colonnes.
Essayons
select id, val into copy_from_with_id_2 from with_id;
Oups, même dans ce cas, id est une colonne d'identité.
Ce que je veux c'est une table comme
create table without_id (
id int,
val varchar(30)
);
Le format de new_table est déterminé en évaluant les expressions dans la liste de sélection. Les colonnes de new_table sont créées dans l'ordre spécifié par la liste de sélection. Chaque colonne de new_table a le même nom, type de données, nullité et valeur que l'expression correspondante dans la liste de sélection. La propriété IDENTITY d'une colonne est transférée sauf dans les conditions définies dans "Utilisation des colonnes d'identité" dans la section Remarques.
En bas de la page:
Lorsqu'une colonne d'identité existante est sélectionnée dans une nouvelle table, la nouvelle colonne hérite de la propriété IDENTITY, sauf si l'une des conditions suivantes est remplie:
- L'instruction SELECT contient une jointure, une clause GROUP BY ou une fonction d'agrégation.
- Plusieurs instructions SELECT sont jointes à l'aide de UNION.
- La colonne d'identité est répertoriée plusieurs fois dans la liste de sélection.
- La colonne d'identité fait partie d'une expression.
- La colonne d'identité provient d'une source de données distante.
Si l'une de ces conditions est remplie, la colonne est créée NOT NULL au lieu d'hériter de la propriété IDENTITY. Si une colonne d'identité est requise dans la nouvelle table mais qu'une telle colonne n'est pas disponible, ou si vous souhaitez une valeur de départ ou d'incrément différente de la colonne d'identité source, définissez la colonne dans la liste de sélection à l'aide de la fonction IDENTITY. Voir "Création d'une colonne d'identité à l'aide de la fonction IDENTITY" dans la section Exemples ci-dessous.
Donc ... vous pourriez théoriquement vous en sortir avec:
select id, val
into copy_from_with_id_2
from with_id
union all
select 0, 'test_row'
where 1 = 0;
Il serait important de commenter ce code pour l'expliquer, de peur qu'il ne soit supprimé la prochaine fois que quelqu'un le regarde.
Inspiré par la réponse d'Erics, j'ai trouvé la solution suivante qui ne dépend que des noms de table et n'utilise aucun nom de colonne spécifique:
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;
Modifier
Il est même possible d’améliorer
select * into without_id from with_id
union all
select * from with_id where 1 = 0
;
Vous pouvez utiliser une jointure pour créer et remplir la nouvelle table en une seule fois:
SELECT
t.*
INTO
dbo.NewTable
FROM
dbo.TableWithIdentity AS t
LEFT JOIN dbo.TableWithIdentity ON 1 = 0
;
En raison de l 1 = 0
condition, le côté droit n'aura aucune correspondance et empêchera ainsi la duplication des lignes du côté gauche, et comme il s'agit d'une jointure externe, les lignes du côté gauche ne seront pas éliminées non plus. Enfin, car il s'agit d'une jointure, la propriété IDENTITY est supprimée.
Par conséquent, la sélection des seules colonnes de gauche produira une copie exacte de dbo.TableWithIdentity uniquement sur les données, c'est-à-dire avec la propriété IDENTITY supprimée.
Cela étant dit, Max Vernon a soulevé un point valable dans un commentaire qui mérite d'être gardé à l'esprit. Si vous regardez le plan d'exécution de la requête ci-dessus:
vous remarquerez que la table source n'est mentionnée qu'une seule fois dans le plan d'exécution. L'autre instance a été éliminée par l'optimiseur.
Ainsi, si l'optimiseur peut établir correctement que le côté droit de la jointure n'est pas nécessaire dans le plan, il devrait être raisonnable de s'attendre à ce que dans une future version de SQL Server, il puisse être en mesure de comprendre que la propriété IDENTITY n'a pas besoin d'être supprimé non plus, car il n'y a plus d'autre colonne IDENTITY dans l'ensemble de lignes source selon le plan de requête. Cela signifie que la requête ci-dessus peut cesser de fonctionner comme prévu à un moment donné.
Mais, comme correctement noté par ypercubeᵀᴹ , jusqu'ici le manuel a explicitement déclaré que s'il y a une jointure, la propriété IDENTITY n'est pas conservée:
Lorsqu'une colonne d'identité existante est sélectionnée dans une nouvelle table, la nouvelle colonne hérite de la propriété IDENTITY, sauf si [...] [t] l'instruction SELECT contient une jointure.
Donc, tant que le manuel le mentionne, nous pouvons probablement être assurés que le comportement restera le même.
Félicitations à Shaneis et ypercubeᵀᴹ pour avoir évoqué un sujet connexe dans le chat.
Essayez ce code ..
SELECT isnull(Tablename_old.IDENTITYCOL + 0, -1) AS 'New Identity Column'
INTO dbo.TableName_new
FROM dbo.TableName_old
L'appel ISNULL
garantit que la nouvelle colonne est créée avec NOT NULL
nullabilité.
Juste pour montrer une manière différente:
Vous pouvez utiliser un serveur lié.
SELECT *
INTO without_id
FROM [linked_server].[source_db].dbo.[with_id];
Vous pouvez créer temporairement un serveur lié au serveur local en utilisant ceci:
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;
À ce stade, vous exécutez le select * into
code, référençant le localserver
nom du serveur lié en quatre parties:
SELECT *
INTO without_id
FROM [localserver].[source_db].dbo.[with_id];
Après cela, nettoyez le serveur lié localserver
avec ceci:
EXEC sp_dropserver @server = 'localserver'
, @droplogins = 'droplogins';
Ou, vous pouvez utiliser la syntaxe OPENQUERY
SELECT *
INTO without_id
FROM OPENQUERY([linked_server], 'SELECT * FROM [source_db].dbo.[with_id]');
La propriété d'identité n'est pas transférée si l'instruction select contient une jointure, et ainsi
select a.* into without_id from with_id a inner join with_id b on 1 = 0;
donnera également le comportement souhaité (de la colonne id
copiée pour ne pas conserver la propriété IDENTITY
. Cependant, cela aura pour effet secondaire de ne copier aucune ligne du tout! (comme avec d'autres vous devrez alors faire:
insert into without_id select * from with_id;
(merci AakashM!)
Le moyen le plus simple consiste à intégrer la colonne à une expression.
Exemple:
Si la table dbo.Employee a une identité sur la colonne ID, dans l'exemple ci-dessous, la table temporaire #t aura également une colonne IDENTITY on ID.
--temp table has IDENTITY
select ID, Name
into #t
from dbo.Employee
Modifiez ceci pour appliquer une expression à ID et vous n'aurez plus de colonne IDENTITY on ID. Dans ce cas, nous appliquons un simple ajout à la colonne ID.
--no IDENTITY
select ID = ID + 0, Name
into #t
from dbo.Employee
D'autres exemples d'expressions pour d'autres types de données peuvent inclure: convert (), la concaténation de chaînes ou Isnull ()
Parfois, vous souhaitez insérer à partir d'une table où vous ne savez pas (ou ne vous souciez pas) si la colonne a été créée à l'aide d'IDENTITY ou non. Ce n'est peut-être même pas une colonne entière avec laquelle vous travaillez. Dans ce cas, les éléments suivants fonctionneront:
SELECT TOP(0) ISNULL([col],NULL) AS [col], ... INTO [table2] FROM [table1]
ALTER TABLE [table2] REBUILD WITH (DATA_COMPRESSION=page)
INSERT INTO [table2] ...
ISNULL supprimera l'attribut IDENTITY de la colonne mais l'insérera avec le même nom et le même type que la colonne d'origine et le rendra non annulable. TOP (0) créera un tableau vide que vous pourrez ensuite utiliser pour insérer des lignes sélectionnées dans. Vous pouvez également compresser la table avant d'insérer des données si nécessaire.
select convert(int, id) as id, val
into copy_from_with_id_without_id
from with_id;
supprimera l'identité.
L'inconvénient est que id
devient nullable mais vous pouvez ajouter cette contrainte.