Überprüfen Sie, ob eine temporäre Tabelle vorhanden ist, und löschen Sie sie, bevor Sie eine temporäre Tabelle erstellen

661

Ich verwende den folgenden Code, um zu überprüfen, ob die temporäre Tabelle vorhanden ist, und die Tabelle zu löschen, falls sie vorhanden ist, bevor ich sie erneut erstelle. Es funktioniert gut, solange ich die Spalten nicht ändere. Wenn ich später eine Spalte hinzufüge, wird der Fehler "ungültige Spalte" angezeigt. Bitte lassen Sie mich wissen, was ich falsch mache.

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
quelle
Wo fügen Sie die Spalte hinzu? Können Sie den genauen Code veröffentlichen, der Ihnen einen Fehler gibt?
Makros
Ich füge die Spalte der Tabelle #Results hinzu. Wenn Sie den obigen Code kopieren und zum ersten Mal ausführen, wird kein Fehler angezeigt. Wenn Sie nun der temporären Tabelle eine Spalte hinzufügen und die Spalte der select-Anweisung hinzufügen, wird angezeigt, dass die Spalte nicht gefunden wurde (oder so ähnlich).
Sridhar
22
Verwenden Sie das folgende Muster : BEGIN TRANSACTION; CREATE TABLE #Results; ...; DROP TABLE #Results; COMMIT. Wenn die Transaktion erfolgreich ist, wird die Tabelle entfernt. Wenn dies fehlschlägt, wird auch die Tabelle gelöscht (da sie innerhalb der Transaktion erstellt wurde). In jedem Fall: Sie müssen nicht überprüfen, ob die Tabelle bereits vorhanden ist.
Heinzi
1
Sieht so aus, als ob Sie nur GO-Anweisungen benötigen.
Sam Yi

Antworten:

732

Ich kann den Fehler nicht reproduzieren.

Vielleicht verstehe ich das Problem nicht.

Folgendes funktioniert in SQL Server 2005 einwandfrei, wobei die zusätzliche Spalte "foo" im zweiten Auswahlergebnis angezeigt wird:

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
quelle
1
WENN OBJECT_ID ('tempdb .. # Results') NICHT NULL ist. DROP TABLE # Results` CREATE TABLE Spaltenfeld-ID am Ende. Ändern Sie die select-Anweisung, um die Feld-ID einzuschließen, und führen Sie sie aus.
Sridhar
28
'tempdb..#name'ist genau das, was ich brauchte. Ich benutzte 'dbo.#name', wie ein Dummkopf. Ich bekomme das tempdbTeil, aber was ist mit den Doppelpunkten?
Conrad.Dean
77
@ Conrad.Dean Doppelpunkt ist eine Abkürzung für .dbo.
deutschZuid
32
@deutschZuid es ist genauer zu sagen, dass double dot das Standardschema des Benutzers ist, was normalerweise dbo ist (was keine gute Idee ist, dbo zum Standardschema für Benutzer zu machen, aber normalerweise geht es so)
jcollum
8
Ihr Code unterscheidet sich so stark vom OP, dass Ihre Anweisung "Kann nicht reproduzieren" bedeutungslos ist. Ich freue mich für Sie, dass Sie es anders gemacht haben.
Gerard ONeill
85

Die Aussage sollte in der Reihenfolge sein

  1. Ändern Sie die Anweisung für die Tabelle
  2. GEHEN
  3. Anweisung auswählen.

Ohne 'GO' dazwischen wird das Ganze als ein einziges Skript betrachtet, und wenn die select-Anweisung nach der Spalte sucht, wird sie nicht gefunden.

Bei 'GO' wird der Teil des Skripts bis zu 'GO' als ein einzelner Stapel betrachtet und ausgeführt, bevor nach 'GO' die Abfrage aufgerufen wird.

Sicherheitsdatenblatt
quelle
7
Dies sollte als die richtige Antwort markiert werden. Es ist nicht so, dass SELECT tatsächlich vor der Erstellungs-Tabelle ausgeführt wird. Es ist so, dass es vor der Ausführung analysiert wird und einen Fehler auslöst, da eine vorhandene Tabelle mit dem Namen #Results vorhanden ist, in der die Spalte FieldId noch nicht vorhanden ist Mal, wenn die select-Anweisung analysiert wird. Durch Hinzufügen eines GO wird die Abfrage in Stapel aufgeteilt, die jeweils separat analysiert und ausgeführt werden.
Davos
2
Ich kann die Ungleichheit der Stimmen zwischen dieser und der Top-Antwort nicht glauben, die den Code so sehr verändert hat - ohne zu erklären, warum -, dass er als Antwort bedeutungslos war.
underscore_d
63

Anstatt droppingdie temporäre Tabelle zu erstellen und neu zu erstellen, können Sie sie truncatewiederverwenden

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

Wenn Sie die folgende Syntax verwenden Sql Server 2016oder Azure Sql Databasedann verwenden, um die temporäre Tabelle zu löschen und neu zu erstellen. Mehr Infos hier MSDN

Syntax

DROP TABLE [IF EXISTS] [Datenbankname. [Schemaname]. | Schemaname. ] Tabellenname [, ... n]

Abfrage:

DROP TABLE IF EXISTS tempdb.dbo.#Results
CREATE TABLE #Results
  (
   Company             CHAR(3),
   StepId              TINYINT,
   FieldId             TINYINT,
  )
P ரதீப்
quelle
Es scheint, dass die truncate/reuseMethode effizienter wäre als die DROP TABLE IF EXISTSon Sql Server 2016und Azure Sql Databaseauch. Ist das nicht der Fall?
JDawg
@prdp Warum schlagen Sie DROP TABLE IF ExistsSQL 2016 oder Azure vor? Die Syntax ist ab SQL 2008 verfügbar. Siehe MSDN-Link in Ihrer Antwort? Leistungsfaktor?
HappyTown
4
Keine Ursache. Ich habe jetzt festgestellt, DROP TABLEdass SQL Server 2008 unterstützt wird, aber die IF EXISTSKlausel wurde 2016 eingeführt.
HappyTown
1
Ich benutze INTO: Wählen Sie * INTO #HistoricoUserTable von dbo.HistoricoUser
Kiquenet
54

Ich denke, das Problem ist, dass Sie dazwischen eine GO-Anweisung hinzufügen müssen, um die Ausführung in Stapel zu unterteilen. Da das zweite Löschskript dh IF OBJECT_ID('tempdb..#Results') IS NOT NULL DROP TABLE #Resultsdie temporäre Tabelle nicht als Teil eines einzelnen Stapels gelöscht hat. Können Sie bitte das folgende Skript versuchen.

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
quelle
1
Bemerkenswert; tempdb..im obigen Code ist sehr wichtig. Es muss vor dem Namen Ihrer temporären Tabelle stehen. Nur zu überprüfen OBJECT_ID('#Results')ist nicht genug. Temporäre Tabellen werden in der TempDB-Datenbank gespeichert. Per Microsoft: Die TempDB-Systemdatenbank ist eine globale Ressource, die allen Benutzern zur Verfügung steht, die mit der Instanz von SQL Server verbunden oder mit der SQL-Datenbank verbunden sind
iCode
Danke, @iCode. Das ist der Schlüssel zum Löschen der temporären Tabellen: Es muss gemacht werden tempdboder es wird nicht weg sein.
Alex
37

Dies könnte mit einer einzigen Codezeile erreicht werden:

IF OBJECT_ID('tempdb..#tempTableName') IS NOT NULL DROP TABLE #tempTableName;   
S Krishna
quelle
1
Ich muss mir das jeden Tag ansehen
Ab Bennett
28

Dies funktionierte für mich: 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
quelle
1
Dies ist nur eine andere Syntax für das Löschen der bedingten Tabelle. Es ist interessant, löst aber nicht die Frage des OP und das meiste davon ist überflüssig. Wenn Sie nur überprüfen, ob OBJECT_ID (N'tempdb .. # Results ') nicht null ist, reicht dies aus, um zu beweisen, dass das Objekt bereits vorhanden ist.
Davos
21

Nur ein kleiner Kommentar von meiner Seite, da das OBJECT_IDbei mir nicht funktioniert. Das wird immer zurückgegeben

`#tempTable existiert nicht

..even obwohl es tut exist. Ich habe gerade festgestellt, dass es unter einem anderen Namen gespeichert ist (durch _Unterstriche nachfixiert ), wie folgt :

#tempTable________

Das funktioniert gut für mich:

IF EXISTS(SELECT [name] FROM tempdb.sys.tables WHERE [name] like '#tempTable%') BEGIN
   DROP TABLE #tempTable;
END;
Ivan Sivak
quelle
6
Achtung: Dieser Code erkennt eine Tabelle, wenn sie von einem Thread erstellt wurde. Einzelne # temporäre Tabellen werden separat pro Thread / Aufrufer für einen gespeicherten Prozess erstellt, weshalb der Name unterstrichen wird, sodass pro Thread / Prozess eine andere Kopie vorhanden ist. Die Object_ID sollte für den aktuellen Thread in Ordnung sein, solange Sie sich in SQL 2005 oder höher befinden.
Bytemaster
12

Jetzt können Sie die folgende Syntax verwenden, wenn Sie eine der neuen Versionen von SQL Server (2016+) verwenden.

DROP TABLE IF EXISTS schema.yourtable(even temporary tables #...)
Othman Dahbi-Skali
quelle
1
Ich benutze SSMS 17.3 und das gibtIncorrect syntax near the keyword 'IF'.
StingyJack
7
@StingyJack Da die SQL-Syntax nicht mit der SSMS-Version, sondern mit der SQL Server-Version zusammenhängt. Die IF [NOT] EXISTSKlausel ist in SQL Server 2016 verfügbar. Es spielt keine Rolle, welche SSMS-Version Sie verwenden.
Pred
10

pmac72 verwendet GO, um die Abfrage in und verwendet ALTER.

Sie scheinen denselben Stapel auszuführen, führen ihn jedoch nach dem Ändern zweimal aus: DROP ... CREATE ... edit ... DROP ... CREATE ..

Schreiben Sie vielleicht Ihren genauen Code, damit wir sehen können, was los ist.

gbn
quelle
7

Normalerweise habe ich diesen Fehler festgestellt, wenn ich die temporäre Tabelle bereits erstellt habe. Der Code, der die SQL-Anweisung auf Fehler überprüft, erkennt die "alte" temporäre Tabelle und gibt eine Fehlzählung der Anzahl der Spalten in späteren Anweisungen zurück, als ob die temporäre Tabelle nie gelöscht worden wäre.

Nachdem Sie die Anzahl der Spalten in einer temporären Tabelle geändert haben, nachdem Sie bereits eine Version mit weniger Spalten erstellt haben, löschen Sie die Tabelle und führen Sie DANN Ihre Abfrage aus.

Jacob Griffin
quelle
6

Ich habe kürzlich gesehen, wie ein DBA etwas Ähnliches getan hat:

begin try
    drop table #temp
end try

begin catch 
    print 'table does not exist'
end catch 

create table #temp(a int, b int)
anonxen
quelle
2
Diese try-Anweisung würde andere Fehler abfangen, die beim Versuch, die Tabelle zu löschen, auftreten könnten. Dieser Code geht davon aus, dass der Versuch nur deshalb fehlschlägt, weil die Tabelle nicht vorhanden ist. Es würde wahrscheinlich die meiste Zeit funktionieren, aber ich würde es nicht garantieren. Wenn die try-Anweisung aus einem anderen Grund fehlschlägt, wird beim Erstellen der Tabelle eine Fehlermeldung angezeigt, da dies das eigentliche Problem beim Löschen der Tabelle maskiert hat.
Davos
Das funktioniert, aber schlecht, ich ermutige nicht auf die harte Tour, wenn es eine kluge und perfekte Lösung gibt. Obwohl die OP-Version 2005 angegeben wurde, wird der try catch-Block in älteren Versionen nicht unterstützt
dejjub-AIS
Das andere Problem dabei ist die Ideologie der Verwendung von Try / Catch gegen Logik. Sie können mehr von der Debatte hier sehen: stackoverflow.com/questions/17335217/try-catch-or-if-statement/…
Logixologe
3

Mein Code verwendet eine SourceTabelle, die sich ändert, und eine DestinationTabelle, die diesen Änderungen entsprechen muss.

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

Ja, "ungültige Spalte" Dieser Fehler wurde in der Zeile "Firma, Schritt-ID, Feld-ID, Neue Spalte aus #Ergebnissen auswählen" ausgelöst.

Es gibt zwei Phasen, in denen t-sql ausgeführt wird:

Beim Parsen überprüft der SQL Server in dieser Phase zunächst die Korrektur Ihrer übermittelten SQL-Zeichenfolge, einschließlich der Tabellenspalte, und optimiert Ihre Abfrage für die schnellste Wiedererlangung.

Zweitens: Ausführen und Abrufen der Daten.

Wenn die Tabelle #Results vorhanden ist, überprüft der Analyseprozess, ob die von Ihnen angegebenen Spalten gültig sind oder nicht. Andernfalls (Tabelle existiert nicht) wird beim Parsen die von Ihnen angegebenen Überprüfungsspalten übergeben.

pnbps
quelle
0

Wenn Sie eine Spalte in einer temporären Tabelle ändern, müssen Sie die Tabelle löschen, bevor Sie die Abfrage erneut ausführen. (Ja, es ist ärgerlich. Genau das, was Sie tun müssen.)

Ich habe immer angenommen, dass dies daran liegt, dass die Prüfung "ungültige Spalte" vom Parser durchgeführt wird, bevor die Abfrage ausgeführt wird. Sie basiert also auf den Spalten in der Tabelle, bevor sie gelöscht wird ..... und das haben auch pnbs gesagt.

Woric
quelle