SELECT INTO une variable de table en T-SQL

372

Vous avez une requête SELECT complexe, à partir de laquelle j'aimerais insérer toutes les lignes dans une variable de table, mais T-SQL ne le permet pas.

Dans le même sens, vous ne pouvez pas utiliser une variable de table avec des requêtes SELECT INTO ou INSERT EXEC. http://odetocode.com/Articles/365.aspx

Petit exemple:

declare @userData TABLE(
                        name varchar(30) NOT NULL,
                        oldlocation varchar(30) NOT NULL
                       )

SELECT name, location
INTO @userData
FROM myTable
    INNER JOIN otherTable ON ...
WHERE age > 30

Les données de la variable de table seront ensuite utilisées pour l'insérer / la mettre à jour dans différentes tables (principalement une copie des mêmes données avec des mises à jour mineures). Le but serait de simplement rendre le script un peu plus lisible et plus facilement personnalisable que de le faire SELECT INTOdirectement dans les bonnes tables. Les performances ne sont pas un problème, car elles rowcountsont assez petites et ne sont exécutées manuellement qu'en cas de besoin.
... ou dites-moi simplement si je fais tout mal.

Indrek
la source

Réponses:

601

Essayez quelque chose comme ceci:

DECLARE @userData TABLE(
    name varchar(30) NOT NULL,
    oldlocation varchar(30) NOT NULL
);

INSERT INTO @userData (name, oldlocation)
SELECT name, location FROM myTable
INNER JOIN otherTable ON ...
WHERE age > 30;
CristiC
la source
2
Si vous "SELECT nom, emplacement FROM myTable" comme les valeurs que vous allez insérer dans la table UserData, peu importe si les noms des variables dans la sélection correspondent aux noms dans la définition de la table. Vous sélectionnez «nom» pour accéder à la variable «nom» UserData, mais vous sélectionnez «emplacement» et l'affectez d'une manière ou d'une autre à la variable «ancien emplacement» UserData. SQL va-t-il simplement les mapper automatiquement ou va-t-il lever une sorte d'exception?
Aran Mulholland
Peu importe le nom, seul le type de colonne.
CristiC
5
Wow ce genre de sens mais en même temps l'analyseur en moi se sent un peu offensé :)
Aran Mulholland
Je n'arrive pas à être en mesure d'utiliser cela dans la ou les déclarations UPDATE: lien essentiel
Paul-Sebastian Manole
1
Dans une instruction d'insertion, si vous ne déclarez pas les colonnes explicitement, elles sont mappées dans l'ordre déclaré dans l'instruction create table d'origine, tout comme select * le fait. Ainsi, l'emplacement dans l'instruction select est mappé sur oldlocation dans la table @userData car l'emplacement est en position 2 dans le jeu de résultats de la sélection, et oldlocation est la colonne 2 dans la définition de la table. Cela dit, ne faites jamais ça. Il ne faut pas se fier à l'ordre des colonnes ou des lignes dans la base de données. Soyez toujours explicite à ce sujet.
absmiths
94

Le but de SELECT INTOest (par les documents, mon accent)

Pour créer une nouvelle table à partir de valeurs dans une autre table

Mais vous déjà avez une table cible! Donc ce que tu veux c'est

L' INSERTinstruction ajoute une ou plusieurs nouvelles lignes à une table

Vous pouvez spécifier les valeurs de données des manières suivantes:

...

En utilisant une SELECTsous - requête pour spécifier les valeurs de données pour une ou plusieurs lignes, telles que:

  INSERT INTO MyTable 
 (PriKey, Description)
        SELECT ForeignKey, Description
        FROM SomeView

Et dans cette syntaxe, il est permis MyTabled'être une variable de table.

AakashM
la source
1
Je souhaite vraiment que la réponse acceptée inclue cette information!
Davie Brown
Je reçois MyTable est "Nom d'objet invalide" faisant cela, donc il manque quelque chose à cette réponse.
Mike Flynn
@MikeFlynn MyTableici est un espace réservé pour le nom de votre table réelle . Je ne pense pas qu'il existe de vraies bases de données avec une table nommée MyTable...
AakashM
Et si je veux créer / déclarer une variable de table avec SELECT INTO ...? Par exemple, pour définir les colonnes de la variable de table comme t1.somecolumn, t1.othercolumn, t2. *
Armando
27

Vous pouvez également utiliser des expressions de table communes pour stocker des jeux de données temporaires. Ils sont plus élégants et conviviaux:

WITH userData (name, oldlocation)
AS
(
  SELECT name, location 
  FROM   myTable    INNER JOIN 
         otherTable ON ...
  WHERE  age>30
)
SELECT * 
FROM   userData -- you can also reuse the recordset in subqueries and joins
nanestev
la source
Aime ça! Je vous remercie.
fourpastmidnight
Je ne pense pas que cela fasse une copie, si vous supprimez ou mettez à jour à partir de UserData cela ne supprimera-t-il pas et ne mettra-t-il pas à jour les enregistrements dans vos tables d'origine?
atreeon
Oui, DELETE et UPDATE sur le CTE modifieront la table source tant que le CTE ne référencera pas plusieurs tables à l'aide de jointures, d'unions, etc.
nanestev
2
L'inconvénient est que vous ne pouvez utiliser la table CTE que dans les commandes suivantes. Si vous devez effectuer plusieurs passages dans l'ensemble de résultats pour une raison quelconque, CTE ne fonctionnera pas. L'OP semble impliquer que plusieurs modifications seront apportées, auquel cas cela ne fonctionnera pas - "Les données de la variable de table seront utilisées plus tard pour l'insérer / la mettre à jour dans différentes tables (principalement une copie des mêmes données avec des modifications mineures) mises à jour)."
Tony
16

Vous pouvez essayer d'utiliser des tables temporaires ... si vous ne le faites pas à partir d'une application. (Il peut être correct de l'exécuter manuellement)

SELECT name, location INTO #userData FROM myTable
INNER JOIN otherTable ON ...
WHERE age>30

Vous sautez l'effort pour déclarer la table de cette façon ... Aide pour les requêtes ad hoc ... Cela crée une table temporaire locale qui ne sera pas visible pour les autres sessions à moins que vous ne soyez dans la même session. Peut-être un problème si vous exécutez une requête à partir d'une application.

si vous avez besoin de l'exécuter sur une application, utilisez des variables déclarées de cette façon:

DECLARE @userData TABLE(
    name varchar(30) NOT NULL,
    oldlocation varchar(30) NOT NULL
);

INSERT INTO @userData
SELECT name, location FROM myTable
INNER JOIN otherTable ON ...
WHERE age > 30;

Edit: comme beaucoup d'entre vous ont mentionné la visibilité mise à jour vers la session depuis la connexion. La création de tables temporaires n'est pas une option pour les applications Web, car les sessions peuvent être réutilisées, respectez les variables temporaires dans ces cas

Mulki
la source
2
Désolé, j'ai oublié de mentionner que je n'ai pas de droits pour CREATE TABLE.
Indrek
6
La création d'un temp a un peu plus de frais généraux.
paparazzo
2
l'utilisation de la table temporaire n'est pas toujours sûre. Par exemple, les services Web. Avec des services Web avec une seule connexion pour limiter la connexion maximale sur le serveur ET protéger SQL un peu plus, la table temporaire existera pour CHAQUE requête passant et pourra écraser quelqu'un qui l'utilise actuellement.
Franck
12
@Franck - si vous utilisez une table temporaire globale (deux préfixes de hachage), vous avez raison. Cependant, une table temporaire locale (un préfixe de hachage) sera isolée dans une seule session (alias connexion unique), il n'y aura donc pas les problèmes de concurrence auxquels vous faites allusion sauf si vous utilisez une connexion unique pour toutes les demandes (pas informé). Les implications possibles en termes de performances restent cependant.
maf748
@GazB Bien sûr, toute déclaration ayant un effet secondaire est exclue de l'utilisation dans a function. D'après mon expérience, dans la plupart des cas où quelqu'un pense avoir besoin de telles déclarations, cela signifie en fait qu'il devrait repenser son function- ou du moins refactoriser un procedure. Parlant pour moi, au moins. :-)
underscore_d
8

Essayez d'utiliser INSERTau lieu de SELECT INTO:

INSERT @UserData   
SELECT name, location etc.
Noel Abrahams
la source
5

Créez d'abord une table temporaire:

Étape 1:

create table #tblOm_Temp (

    Name varchar(100),
    Age Int ,
    RollNumber bigint
)

** Étape 2: ** Insérez une valeur dans la table Temp.

insert into #tblom_temp values('Om Pandey',102,1347)

Étape 3: Déclarez une variable de table pour contenir les données de la table temporaire.

declare   @tblOm_Variable table(

    Name Varchar(100),
    Age int,
    RollNumber bigint
)

Étape 4: sélectionnez la valeur dans la table temporaire et insérez-la dans la variable de table.

insert into @tblOm_Variable select * from #tblom_temp

Enfin, la valeur est insérée d'une table temporaire dans la variable Table

Étape 5: Peut vérifier la valeur insérée dans la variable de table.

select * from @tblOm_Variable
404 Introuvable
la source
1

OK, maintenant avec suffisamment d'effort, je suis capable d'insérer dans @table en utilisant ce qui suit:

INSERT @TempWithheldTable SELECT
a.SuspendedReason, a.SuspendedNotes, a.SuspendedBy, a.ReasonCode FROM OPENROWSET (BULK 'C: \ DataBases \ WithHeld.csv', FORMATFILE = N'C: \ DataBases \ Format.txt ',
ERRORFILE = N'C: \ Temp \ MovieLensRatings.txt ') AS a;

L'essentiel ici est de sélectionner les colonnes à insérer.

RahulJha
la source
J'obtiens un message d'erreur de compilation «Doit déclarer la variable de table« @TempWithheldTable »
atreeon
-5

L'une des raisons d'utiliser SELECT INTO est qu'il vous permet d'utiliser IDENTITY:

SELECT IDENTITY(INT,1,1) AS Id, name
INTO #MyTable 
FROM (SELECT name FROM AnotherTable) AS t

Cela ne fonctionnerait pas avec une variable de table, ce qui est dommage ...

MOHCTP
la source
6
Vous pouvez cependant déclarer une variable de table avec une IDENTITYcolonne.
Martin Smith