Controllare se esiste una tabella temporanea ed eliminare se esiste prima di creare una tabella temporanea


663

Sto usando il seguente codice per verificare se esiste la tabella temporanea e rilasciarla se esiste prima di crearla nuovamente. Funziona bene finché non cambio le colonne. Se aggiungo una colonna in seguito, verrà visualizzato un errore che dice "colonna non valida". Per favore fatemi sapere cosa sto facendo di sbagliato.

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

Dove stai aggiungendo la colonna? puoi pubblicare il codice esatto che ti sta dando un errore?
Macro

Sto aggiungendo la colonna alla tabella #Results. Se copi il codice sopra ed eseguilo per la prima volta, non ricevi alcun errore. Ora se aggiungi una colonna alla tabella temporanea e aggiungi la colonna all'istruzione select, dirà colonna non trovata (o qualcosa del genere).
Sridhar,

22
Si consiglia di utilizzare il seguente schema: BEGIN TRANSACTION; CREATE TABLE #Results; ...; DROP TABLE #Results; COMMIT. Se la transazione ha esito positivo, la tabella verrà rimossa. Se fallisce, anche la tabella sparirà (poiché è stata creata all'interno della transazione). In ogni caso: non è necessario verificare se la tabella esiste già.
Heinzi,

1
Sembra che tu abbia solo bisogno delle dichiarazioni GO.
sam yi,

Risposte:


734

Non riesco a riprodurre l'errore.

Forse non capisco il problema.

In SQL Server 2005 funziona correttamente per me, con la colonna "pippo" aggiuntiva che appare nel secondo risultato di selezione:

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

1
SE OBJECT_ID ('tempdb .. # Results') NON È NULL DROP TABLE # Results` CREATE TABLE #Results (Company CHAR (3), StepId INT) seleziona azienda, stepid da #results ora torna all'istruzione create e aggiungi un colonna fieldid nell'istruzione select end.change per includere fieldid ed eseguirlo.
Sridhar,

28
'tempdb..#name'è esattamente quello di cui avevo bisogno. Stavo usando 'dbo.#name', come un pazzo. Ricevo la tempdbparte, ma cosa succede con i punti doppi?
Conrad.Dean,

77
@ Conrad.Dean doppio punto è un'abbreviazione per .dbo.
deutschZuid,

32
@deutschZuid è più preciso affermare che il doppio punto è lo schema predefinito dell'utente, che in genere è dbo (che non è una grande idea, rendendo dbo lo schema predefinito per gli utenti ma di solito è così)
jcollum

8
Il tuo codice è così diverso dall'OP, che la tua affermazione "impossibile riprodurre" non ha senso. Sono felice per te che tu l'abbia fatto funzionare in un modo diverso.
Gerard ONeill,

85

La dichiarazione dovrebbe essere dell'ordine

  1. Modifica l'istruzione per la tabella
  2. PARTIRE
  3. Seleziona la dichiarazione.

Senza "GO" nel mezzo, l'intera cosa verrà considerata come un singolo script e quando l'istruzione select cerca la colonna, non verrà trovata.

Con "GO", considererà la parte dello script fino a "GO" come un singolo batch e verrà eseguita prima di accedere alla query dopo "GO".


7
Questo dovrebbe essere contrassegnato come la risposta corretta. Non è che SELECT sia effettivamente in esecuzione prima della tabella di creazione, è che viene analizzato e genera un errore prima di essere eseguito, perché esiste una tabella esistente chiamata #Results che non ha ancora la colonna FieldId nella volta che viene analizzata l'istruzione select. L'aggiunta di un GO al suo interno separa la query in batch, che vengono ciascuno analizzati ed eseguiti separatamente.
Davos,

2
Non posso credere alla disparità di voti tra questa e la risposta migliore, che ha cambiato così tanto il codice - senza spiegarne il motivo - che non aveva senso come risposta.
underscore_d

63

Invece di droppinge ricreare la tabella temporanea è possibile truncatee riutilizzarla

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

Se si sta utilizzando Sql Server 2016o Azure Sql Databasequindi utilizzare la sintassi seguente per eliminare la tabella temporanea e ricrearla. Maggiori informazioni qui MSDN

Sintassi

DROP TABLE [IF EXISTS] [nome_database. [nome_ schema]. | nome_schema. ] table_name [, ... n]

Query:

DROP TABLE IF EXISTS tempdb.dbo.#Results
CREATE TABLE #Results
  (
   Company             CHAR(3),
   StepId              TINYINT,
   FieldId             TINYINT,
  )

Sembra che il truncate/reusemetodo sarebbe più efficiente di DROP TABLE IF EXISTSon Sql Server 2016e Azure Sql Databasepure. Non è così?
JDawg,

@prdp Perché consigli DROP TABLE IF Existsper SQL 2016 o Azure? La sintassi è disponibile a partire da SQL 2008. Vedi il link MSDN nella tua risposta? Fattore di prestazione?
HappyTown

4
Non importa. Ora ho capito, DROP TABLEè supportato da SQL Server 2008, ma la IF EXISTSclausola è stata introdotta nel 2016.
HappyTown,

1
Uso INTO: seleziona * INTO #HistoricoUserTable da dbo.HistoricoUser
Kiquenet,

54

Penso che il problema sia che devi aggiungere l'istruzione GO in mezzo per separare l'esecuzione in batch. Poiché il secondo script di eliminazione IF OBJECT_ID('tempdb..#Results') IS NOT NULL DROP TABLE #Results, ad esempio, non ha eliminato la tabella temporanea facente parte di un singolo batch. Puoi provare lo script qui sotto.

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

1
Di nota; tempdb..nel codice sopra è molto importante. Deve precedere il nome della tabella temporanea. Il semplice controllo OBJECT_ID('#Results')non è sufficiente. Le tabelle temporanee sono archiviate nel database TempDB. Per Microsoft: il database di sistema TempDB è una risorsa globale disponibile per tutti gli utenti connessi all'istanza di SQL Server o connessi al database SQL
iCode

Grazie, @iCode. Questa è la chiave per eliminare le tabelle temporanee: deve essere eseguita tempdbo non scomparirà.
Alex

37

Questo potrebbe essere realizzato con una singola riga di codice:

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

1
devo guardarlo ogni giorno
Ab Bennett il

28

Questo ha funzionato per me: 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;

1
Questa è solo una sintassi diversa per il drop della tabella condizionale. È interessante ma non risolve la domanda del PO e la maggior parte è ridondante. Se si controlla semplicemente OBJECT_ID (N'tempdb .. # Results ') non è nullo, è sufficiente dimostrare che l'oggetto esiste già.
Davos,

21

Solo un piccolo commento dalla mia parte poiché OBJECT_IDnon funziona per me. Lo restituisce sempre

`#tempTable non esiste

..anche se non esistono. Ho appena scoperto che è memorizzato con un nome diverso (postfisso da caratteri di _sottolineatura) in questo modo:

#tempTable________

Questo funziona bene per me:

IF EXISTS(SELECT [name] FROM tempdb.sys.tables WHERE [name] like '#tempTable%') BEGIN
   DROP TABLE #tempTable;
END;

6
Attenzione: quel codice rileverà una tabella se è stata creata da qualsiasi thread. Le tabelle # temp singole vengono create separatamente per thread / chiamante in un proc memorizzato, motivo per cui i caratteri di sottolineatura nel nome in modo che esista una copia diversa per thread / processo. Object_ID dovrebbe funzionare correttamente per il thread corrente, purché si utilizzi SQL 2005 o versioni successive.
Bytemaster,

12

Ora puoi utilizzare la sintassi seguente se stai utilizzando una delle nuove versioni di SQL Server (2016+).

DROP TABLE IF EXISTS schema.yourtable(even temporary tables #...)

1
Sto usando SSMS 17.3 e questo dàIncorrect syntax near the keyword 'IF'.
StingyJack

7
@StingyJack Perché la sintassi SQL non è correlata alla versione di SSMS, ma alla versione di SQL Server. La IF [NOT] EXISTSclausola è disponibile da SQL Server 2016. Non importa quale versione di SSMS si sta utilizzando.
Pred

10

pmac72 utilizza GO per suddividere la query in batch e utilizza ALTER.

Sembra che tu stia eseguendo lo stesso batch ma eseguendolo due volte dopo averlo modificato: DROP ... CREATE ... edit ... DROP ... CREATE ..

Forse pubblica il tuo codice esatto in modo che possiamo vedere cosa sta succedendo.


7

Di solito ho riscontrato questo errore quando ho già creato la tabella temporanea; il codice che controlla gli errori dell'istruzione SQL vede la "vecchia" tabella temporanea in atto e restituisce un conteggio errato sul numero di colonne nelle istruzioni successive, come se la tabella temporanea non fosse mai stata eliminata.

Dopo aver modificato il numero di colonne in una tabella temporanea dopo aver già creato una versione con meno colonne, rilasciare la tabella e POI eseguire la query.


6

Di recente ho visto un DBA fare qualcosa di simile a questo:

begin try
    drop table #temp
end try

begin catch 
    print 'table does not exist'
end catch 

create table #temp(a int, b int)

2
Questa istruzione try rileverà altri errori che potrebbero verificarsi durante il tentativo di eliminare la tabella. Questo codice presuppone che l'unica ragione per cui il tentativo fallisce è perché la tabella non esiste. Probabilmente funzionerebbe per la maggior parte del tempo, ma non lo garantirei. Se l'istruzione try non riesce per qualche altro motivo, verrà visualizzato un errore durante la creazione della tabella, poiché ciò ha mascherato il vero problema con l'eliminazione della tabella.
Davos,

Funziona ma male, non incoraggio il duro quando c'è una soluzione intelligente e perfetta. Inoltre, sebbene OP abbia specificato la versione 2005, provare a catturare il blocco non è supportato nelle versioni precedenti
dejjub-AIS

L'altro problema con questo è l'ideologia dell'uso della logica try / catch vs. Si può vedere di più del dibattito qui: stackoverflow.com/questions/17335217/try-catch-or-if-statement/...
logixologist

3

Il mio codice utilizza una Sourcetabella che cambia e una Destinationtabella che deve corrispondere a tali modifiche.

-- 
-- 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

1

Sì, "colonna non valida" questo errore generato dalla riga "seleziona azienda, stepid, fieldid, NewColumn da #Results".

Esistono due fasi di esecuzione di t-sql,

in primo luogo, analizzando, in questa fase il server sql verifica la correzione della stringa sql inviata, inclusa la colonna della tabella, e ha ottimizzato la query per un ripristino più rapido.

secondo, correre, recuperare i dati.

Se la tabella #Results esiste, il processo di analisi controllerà che le colonne specificate siano valide o meno, altrimenti l'analisi (la tabella non esiste) verrà superata passando le colonne di verifica come specificato.


0

Quando si modifica una colonna in una tabella temporanea, è necessario eliminare la tabella prima di eseguire nuovamente la query. (Sì, è fastidioso. Proprio quello che devi fare.)

Ho sempre pensato che ciò fosse dovuto al fatto che il controllo "colonna non valida" viene eseguito dal parser prima che venga eseguita la query, quindi si basa sulle colonne nella tabella prima che venga rilasciata ..... ed è quello che dicevano anche i pnbs.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.