INSERT INTO permet SELECT en utilisant une colonne inexistante

Lors de l'exécution de ce qui suit:

CREATE TABLE TableA ( A1 INT, A2 INT ) INSERT INTO TableA (A1, A2) VALUES (1, 2), (3, 4); CREATE TABLE #TempTable1 (ColumnA INT, ColumnB INT); CREATE TABLE #TempTable2 (ColumnA INT, ColumnB INT); INSERT INTO #TempTable1 (ColumnA, ColumnB) VALUES (1, 2); INSERT INTO #TempTable2 SELECT A1, A2 FROM TableA WHERE A1 IN (SELECT ColumnA FROM #TempTable1); SELECT * FROM TableA; SELECT * FROM #TempTable1; SELECT * FROM #TempTable2; 

Les résultats sont les suivants:

 TableA A1 A2 1 2 3 4 #TempTable1 ColumnA ColumnB 1 2 #TempTable2 ColumnA ColumnB 1 2 

Toutefois, si je modifie l'instruction d'insertion # TempTable2 afin qu'elle sélectionne une colonne A1 inexistante à partir de # TempTable1:

 INSERT INTO #TempTable2 SELECT A1, A2 FROM TableA WHERE A1 IN (SELECT A1 FROM #TempTable1); 

Alors # TempTable2 contient toutes datatables de TableA:

 ColumnA ColumnB 1 2 3 4 

Je me request pourquoi l'exécution de l'instruction INSERT ne génère pas d'erreur car la colonne A1 n'existe pas dans # TempTable1. Par exemple, si j'essaie d'append l'instruction suivante:

 SELECT A1 FROM #TempTable1; 

Je reçois:

 Invalid column name 'A1'. 

C'est ce qu'on appelle la correlated sub-query . Référence de la colonne de requête externe dans la Sub-Query

La colonne A1 est référencée à partir de la requête externe (ie) TableA . Donc, lorsque vous exécutez la requête set, cela fonctionne et lorsque vous exécutez seul, cela ne fonctionne pas

Il sera interprété comme

 SELECT A1, A2 FROM TableA A WHERE A1 IN (SELECT A.A1 -- Here check the alias A FROM #TempTable1); 

Normalement, lors de l'utilisation de EXISTS/NOT EXISTS la requête sera corrélée dans la clause where .

 select * from tableA A where exists (select 1 from tableB B where A.Id = B.Id -- A.Id is referred from tableA ) 

Dans votre exemple, il est référencé dans Select

Tandis que les autres réponses répondent déjà aux raisons pour lesquelles vous obtenez les résultats, je pense qu'il y a un autre aspect à couvrir: c'est un très bon exemple de pourquoi vous devriez utiliser des alias de table.

Si vous avez aliasé le code comme suit, vous auriez reçu l'erreur que vous attendiez:

 INSERT INTO #TempTable2 SELECT a.A1, a.A2 FROM TableA a WHERE a.A1 IN (SELECT t.A1 FROM #TempTable1 t); 

Sans alias, SQL Server (et d'autres produits SGBD) sera heureusement par défaut à la colonne qui correspond à un nom que vous avez donné. Cela conduit à des erreurs lorsque les gens font exactement ce que vous avez fait, mais ne remarquent pas le problème. Avec les alias en place, vous recevez simplement une erreur, notez le problème et corrigez-le.

 INSERT INTO #TempTable2 SELECT A1, A2 FROM TableA WHERE A1 IN (SELECT A1 FROM #TempTable1); 

Dans cette requête, SELECT A1 FROM #TempTable1 est une sous-requête. A1 n'est pas trouvé dans #TempTable1 mais vous l'utilisez comme sous-requête pour chaque ligne de TableA donc A1 est une valeur tirée de la table supérieure. Votre sous-requête est équivalente à WHERE 1 IN (SELECT 1 FROM #TempTable1); car A1 est une valeur constante pour chaque exécution de la sous-requête.