Vérifiez s'il existe une table temporaire et supprimez-la avant de créer une table temporaire

663

J'utilise le code suivant pour vérifier si la table temporaire existe et supprimer la table si elle existe avant de la créer à nouveau. Cela fonctionne très bien tant que je ne change pas les colonnes. Si j'ajoute une colonne plus tard, cela donnera une erreur disant "colonne invalide". Veuillez me faire savoir ce que je fais mal.

IF OBJECT_ID('tempdb..#Results') IS NOT NULL
    DROP TABLE #Results

CREATE TABLE #Results
(
    Company                CHAR(3),
    StepId                TINYINT,
    FieldId                TINYINT,
)

select company, stepid, fieldid from #Results

--Works fine to this point

IF OBJECT_ID('tempdb..#Results') IS NOT NULL
    DROP TABLE #Results

CREATE TABLE #Results
(
    Company                CHAR(3),
    StepId                TINYINT,
    FieldId                TINYINT,
    NewColumn            NVARCHAR(50)
)

select company, stepid, fieldid, NewColumn from #Results

--Does not work
Sridhar
la source
Où ajoutez-vous la colonne? pouvez-vous publier le code exact qui vous donne une erreur?
Macros
J'ajoute la colonne à la table #Results. Si vous copiez le code ci-dessus et l'exécutez pour la première fois, vous n'obtiendrez aucune erreur. Maintenant, si vous ajoutez une colonne à la table temporaire et ajoutez la colonne à l'instruction select, il dira colonne non trouvée (ou quelque chose comme ça).
Sridhar
22
Pensez à utiliser le schéma suivant: BEGIN TRANSACTION; CREATE TABLE #Results; ...; DROP TABLE #Results; COMMIT. Si la transaction réussit, la table sera supprimée. S'il échoue, la table disparaîtra également (car elle a été créée dans la transaction). Dans tous les cas: pas besoin de vérifier si la table existe déjà.
Heinzi
1
On dirait que vous avez juste besoin des instructions GO.
sam yi

Réponses:

734

Je ne peux pas reproduire l'erreur.

Je ne comprends peut-être pas le problème.

Ce qui suit fonctionne bien pour moi dans SQL Server 2005, avec la colonne "foo" supplémentaire apparaissant dans le deuxième résultat de sélection:

IF OBJECT_ID('tempdb..#Results') IS NOT NULL DROP TABLE #Results
GO
CREATE TABLE #Results ( Company CHAR(3), StepId TINYINT, FieldId TINYINT )
GO
select company, stepid, fieldid from #Results
GO
ALTER TABLE #Results ADD foo VARCHAR(50) NULL
GO
select company, stepid, fieldid, foo from #Results
GO
IF OBJECT_ID('tempdb..#Results') IS NOT NULL DROP TABLE #Results
GO
pmac72
la source
1
SI OBJECT_ID ('tempdb .. # Results') N'EST PAS NULL DROP TABLE # Results` CREATE TABLE #Results (Company CHAR (3), StepId INT) sélectionnez company, stepid de #results maintenant retournez à l'instruction create et ajoutez un colonne fieldid à l'instruction end.change select pour inclure fieldid et l'exécuter.
Sridhar
28
'tempdb..#name'c'est exactement ce dont j'avais besoin. J'utilisais 'dbo.#name', comme un idiot. J'obtiens la tempdbpièce, mais qu'est-ce qui se passe avec les doubles points?
Conrad.Dean
77
@ Conrad.Dean double dot est une abréviation de .dbo.
deutschZuid
32
@deutschZuid, il est plus précis de dire que le double point est le schéma par défaut de l'utilisateur, qui est généralement dbo (ce qui n'est pas une bonne idée, faisant de dbo le schéma par défaut pour les utilisateurs, mais c'est généralement le cas)
jcollum
8
Votre code est tellement différent de l'OP, que votre déclaration «ne peut pas reproduire» n'a pas de sens. Je suis heureux pour vous que vous l'ayez fait fonctionner différemment.
Gerard ONeill
85

La déclaration doit être de l'ordre

  1. Instruction Alter pour la table
  2. ALLER
  3. Sélectionnez l'instruction.

Sans 'GO' entre les deux, le tout sera considéré comme un seul script et lorsque l'instruction select recherchera la colonne, elle ne sera pas trouvée.

Avec 'GO', il considérera la partie du script jusqu'à 'GO' comme un seul lot et s'exécutera avant d'entrer dans la requête après 'GO'.

SDS
la source
7
Cela devrait être marqué comme la bonne réponse. Ce n'est pas que le SELECT va réellement s'exécuter avant la table de création, c'est qu'il est en cours d'analyse et génère une erreur avant d'être exécuté, car il existe une table existante appelée #Results qui n'a pas encore la colonne FieldId à la moment où l'instruction select est analysée. L'ajout d'un GO sépare la requête en lots, qui sont chacun analysés et exécutés séparément.
Davos
2
Je ne peux pas croire la disparité des votes entre cette réponse et la première, qui a tellement changé le code - sans expliquer pourquoi - que cela n'avait pas de sens en tant que réponse.
underscore_d
63

Au lieu de droppingrecréer la table temporaire, vous pouvez la truncateréutiliser

IF OBJECT_ID('tempdb..#Results') IS NOT NULL
    Truncate TABLE #Results
else
    CREATE TABLE #Results
    (
        Company             CHAR(3),
        StepId              TINYINT,
        FieldId             TINYINT,
    )

Si vous utilisez Sql Server 2016ou Azure Sql Databaseutilisez la syntaxe ci-dessous pour supprimer la table temporaire et la recréer. Plus d'infos ici MSDN

Syntaxe

DROP TABLE [IF EXISTS] [nom_base de données. [nom_schéma]. | nom_schéma. ] nom_table [, ... n]

Requete:

DROP TABLE IF EXISTS tempdb.dbo.#Results
CREATE TABLE #Results
  (
   Company             CHAR(3),
   StepId              TINYINT,
   FieldId             TINYINT,
  )
P ரதீப்
la source
Il semble que la truncate/reuseméthode serait plus efficace que la DROP TABLE IF EXISTSsur Sql Server 2016et Azure Sql Databaseaussi bien. Ce n'est pas le cas?
JDawg
@prdp Pourquoi suggérez-vous DROP TABLE IF Existspour SQL 2016 ou Azure? La syntaxe est disponible à partir de SQL 2008. Voir le lien MSDN dans votre réponse? Facteur de performance?
HappyTown
4
Ça ne fait rien. Je me suis maintenant rendu compte, DROP TABLEest pris en charge par SQL Server 2008, mais la IF EXISTSclause a été introduite en 2016.
HappyTown
1
J'utilise INTO: sélectionnez * INTO #HistoricoUserTable dans dbo.HistoricoUser
Kiquenet
54

Je pense que le problème est que vous devez ajouter une instruction GO entre les deux pour séparer l'exécution en lots. Comme le deuxième script de suppression, c'est-à IF OBJECT_ID('tempdb..#Results') IS NOT NULL DROP TABLE #Results- dire qu'il n'a pas supprimé la table temporaire faisant partie d'un seul lot. Pouvez-vous s'il vous plaît essayer le script ci-dessous.

IF OBJECT_ID('tempdb..#Results') IS NOT NULL
    DROP TABLE #Results

CREATE TABLE #Results
(
    Company                CHAR(3),
    StepId                TINYINT,
    FieldId                TINYINT,
)

GO

select company, stepid, fieldid from #Results

IF OBJECT_ID('tempdb..#Results') IS NOT NULL
DROP TABLE #Results

CREATE TABLE #Results
(
    Company                CHAR(3),
    StepId                TINYINT,
    FieldId                TINYINT,
    NewColumn            NVARCHAR(50)
)

GO

select company, stepid, fieldid, NewColumn from #Results
vikas
la source
1
À noter; tempdb..dans le code ci-dessus est très important. Il doit précéder le nom de votre table temporaire. Une simple vérification OBJECT_ID('#Results')ne suffit pas. Les tables temporaires sont stockées dans la base de données TempDB. Par Microsoft: la base de données système TempDB est une ressource globale qui est disponible pour tous les utilisateurs connectés à l'instance de SQL Server ou connectés à la base de données SQL
iCode
Merci, @iCode. C'est la clé pour supprimer les tables temporaires: cela doit être fait tempdbou cela ne disparaîtra pas.
Alex
37

Cela pourrait être accompli avec une seule ligne de code:

IF OBJECT_ID('tempdb..#tempTableName') IS NOT NULL DROP TABLE #tempTableName;   
S Krishna
la source
1
je dois regarder cela tous les jours
Ab Bennett
28

Cela a fonctionné pour moi: social.msdn.microsoft.com/Forums/en/transactsql/thread/02c6da90-954d-487d-a823-e24b891ec1b0?prof=required

if exists (
    select  * from tempdb.dbo.sysobjects o
    where o.xtype in ('U') 

   and o.id = object_id(N'tempdb..#tempTable')
)
DROP TABLE #tempTable;
user219628
la source
1
Il s'agit simplement d'une syntaxe différente pour la suppression de table conditionnelle. C'est intéressant mais ne résout pas la question du PO, et la plupart est redondante. Si vous vérifiez simplement que OBJECT_ID (N'tempdb .. # Results ') n'est pas nul, cela suffit pour prouver que l'objet existe déjà.
Davos
21

Juste un petit commentaire de mon côté car OBJECT_IDcela ne fonctionne pas pour moi. Il revient toujours que

`#tempTable n'existe pas

..even bien qu'il ne exist. Je viens de découvrir qu'il est stocké avec un nom différent (postfixé par des traits de _soulignement) comme ceci:

#tempTable________

Cela fonctionne bien pour moi:

IF EXISTS(SELECT [name] FROM tempdb.sys.tables WHERE [name] like '#tempTable%') BEGIN
   DROP TABLE #tempTable;
END;
Ivan Sivak
la source
6
Attention: ce code détectera une table si elle a été créée par un thread. Des tables temporaires # uniques sont créées séparément par thread / appelant dans un proc stocké, c'est pourquoi les traits de soulignement du nom de sorte qu'une copie différente existe par thread / processus. Le Object_ID devrait fonctionner correctement pour le thread actuel, tant que vous êtes sur SQL 2005 ou version ultérieure.
Bytemaster
12

Vous pouvez maintenant utiliser la syntaxe ci-dessous si vous utilisez l'une des nouvelles versions de SQL Server (2016+).

DROP TABLE IF EXISTS schema.yourtable(even temporary tables #...)
Othman Dahbi-Skali
la source
1
J'utilise SSMS 17.3 et cela donneIncorrect syntax near the keyword 'IF'.
StingyJack
7
@StingyJack Parce que la syntaxe SQL n'est pas liée à la version SSMS, mais à la version SQL Server. La IF [NOT] EXISTSclause est disponible à partir de SQL Server 2016. Peu importe la version SSMS que vous utilisez.
Pred
10

pmac72 utilise GO pour décomposer la requête en lots et utilise un ALTER.

Vous semblez exécuter le même lot mais l'exécuter deux fois après l'avoir modifié: DROP ... CREATE ... edit ... DROP ... CREATE ..

Peut-être postez votre code exact afin que nous puissions voir ce qui se passe.

gbn
la source
7

Je frappe généralement cette erreur lorsque j'ai déjà créé la table temporaire; le code qui vérifie les erreurs de l'instruction SQL voit la «vieille» table temporaire en place et retourne une erreur de calcul sur le nombre de colonnes dans les instructions ultérieures, comme si la table temporaire n'était jamais supprimée.

Après avoir modifié le nombre de colonnes dans une table temporaire après avoir déjà créé une version avec moins de colonnes, supprimez la table et exécutez ensuite votre requête.

Jacob Griffin
la source
6

J'ai récemment vu un DBA faire quelque chose de similaire à ceci:

begin try
    drop table #temp
end try

begin catch 
    print 'table does not exist'
end catch 

create table #temp(a int, b int)
anonxen
la source
2
Cette instruction try intercepterait d'autres erreurs pouvant survenir lors de la tentative de suppression de la table. Ce code suppose que la seule raison de l'échec de l'essai est que la table n'existe pas. Cela fonctionnerait probablement la plupart du temps, mais je ne le garantirais pas. Si l'instruction try échoue pour une autre raison, vous obtiendrez une erreur lors de la création de la table, car cela a masqué le véritable problème de suppression de la table.
Davos
Cela fonctionne mais mauvais, je n'encourage pas à la dure quand il y a une solution intelligente et parfaite. Et aussi, bien que l'OP ait spécifié la version 2005, le bloc try catch n'est pas pris en charge dans les anciennes versions
dejjub-AIS
L'autre problème avec cela est l'idéologie de l'utilisation de la logique try / catch vs. Vous pouvez voir plus du débat ici: stackoverflow.com/questions/17335217/try-catch-or-if-statement/…
logixologist
3

Mon code utilise une Sourcetable qui change et une Destinationtable qui doit correspondre à ces changements.

-- 
-- Sample SQL to update only rows in a "Destination" Table
--  based on only rows that have changed in a "Source" table
--


--
-- Drop and Create a Temp Table to use as the "Source" Table
--
IF OBJECT_ID('tempdb..#tSource') IS NOT NULL drop table #tSource
create table #tSource (Col1 int, Col2 int, Col3 int, Col4 int)

--
-- Insert some values into the source
--
Insert #tSource (Col1, Col2, Col3, Col4) Values(1,1,1,1)
Insert #tSource (Col1, Col2, Col3, Col4) Values(2,1,1,2)
Insert #tSource (Col1, Col2, Col3, Col4) Values(3,1,1,3)
Insert #tSource (Col1, Col2, Col3, Col4) Values(4,1,1,4)
Insert #tSource (Col1, Col2, Col3, Col4) Values(5,1,1,5)
Insert #tSource (Col1, Col2, Col3, Col4) Values(6,1,1,6)

--
-- Drop and Create a Temp Table to use as the "Destination" Table
--
IF OBJECT_ID('tempdb..#tDest') IS NOT NULL drop Table #tDest
create table #tDest (Col1 int, Col2 int, Col3 int, Col4 int)

--
-- Add all Rows from the Source to the Destination
--
Insert #tDest
Select Col1, Col2, Col3, Col4 from #tSource


--
-- Look at both tables to see that they are the same
--
select *
from #tSource
Select *
from #tDest

--
-- Make some changes to the Source
--
update #tSource
    Set Col3=19
    Where Col1=1
update #tSource
    Set Col3=29
    Where Col1=2
update #tSource
    Set Col2=38
    Where Col1=3
update #tSource
    Set Col2=48
    Where Col1=4

--
-- Look at the Differences
-- Note: Only 4 rows are different. 2 Rows have remained the same.
--
Select Col1, Col2, Col3, Col4
from #tSource
except
Select Col1, Col2, Col3, Col4
from #tDest

--
-- Update only the rows that have changed
-- Note: I am using Col1 like an ID column
--
Update #tDest
    Set Col2=S.Col2,
        Col3=S.Col3,
        Col4=S.Col4
From    (   Select Col1, Col2, Col3, Col4
            from #tSource
            except
            Select Col1, Col2, Col3, Col4
            from #tDest
        ) S
Where #tDest.Col1=S.Col1 

--
-- Look at the tables again to see that
--  the destination table has changed to match
--  the source table.

select *
from #tSource
Select *
from #tDest

--
-- Clean Up
--
drop table #tSource
drop table #tDest
Mike Lewis
la source
1

Oui, "colonne non valide", cette erreur est survenue à partir de la ligne "sélectionnez la société, stepid, fieldid, NewColumn de #Results".

Il y a deux phases d'exécution de t-sql,

tout d'abord, en analysant, dans cette phase, le serveur SQL vérifie la correction de votre chaîne SQL soumise, y compris la colonne de table, et optimise votre requête pour une récupération plus rapide.

deuxièmement, exécuter, récupérer les données.

Si la table #Results existe, le processus d'analyse vérifiera que les colonnes que vous avez spécifiées sont valides ou non, sinon (la table n'existe pas) l'analyse sera passée par les colonnes de vérification comme vous l'avez spécifié.

pnbps
la source
0

Lorsque vous modifiez une colonne dans une table temporaire, vous devez supprimer la table avant d'exécuter à nouveau la requête. (Oui, c'est ennuyeux. Juste ce que vous avez à faire.)

J'ai toujours supposé que c'est parce que la vérification de la "colonne invalide" est effectuée par l'analyseur avant que la requête ne soit exécutée, elle est donc basée sur les colonnes du tableau avant qu'elle ne soit supprimée ..... et c'est ce que pnbs a également dit.

Woric
la source