Verifique se existe uma tabela temporária e exclua se existir antes de criar uma tabela temporária


663

Estou usando o código a seguir para verificar se a tabela temporária existe e soltar a tabela, se existir, antes de criar novamente. Funciona bem desde que eu não mude as colunas. Se eu adicionar uma coluna posteriormente, ocorrerá um erro dizendo "coluna inválida". Por favor, deixe-me saber o que estou fazendo de errado.

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

Onde você está adicionando a coluna? você pode postar o código exato que está causando um erro?
Macros

Estou adicionando a coluna à tabela #Results. Se você copiar o código acima e executá-lo pela primeira vez, não receberá nenhum erro. Agora, se você adicionar uma coluna à tabela temporária e a coluna à instrução select, será exibida a coluna não encontrada (ou algo parecido).
31410 Sridhar

22
Considere usar o seguinte padrão: BEGIN TRANSACTION; CREATE TABLE #Results; ...; DROP TABLE #Results; COMMIT. Se a transação for bem-sucedida, a tabela será removida. Se falhar, a tabela também desaparecerá (desde que foi criada dentro da transação). De qualquer forma: Não há necessidade de verificar se a tabela já existe.
Heinzi

1
Parece que você só precisa de instruções GO.
sam yi

Respostas:


734

Não consigo reproduzir o erro.

Talvez eu não esteja entendendo o problema.

O seguinte funciona bem para mim no SQL Server 2005, com a coluna "foo" extra aparecendo no segundo resultado selecionado:

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') NÃO É NULL DROP TABLE # Results` CREATE TABLE #Results (Empresa CHAR (3), StepId INT) seleciona a empresa, agora, #ID da resposta, volte para a instrução create e adicione um coluna fieldid na instrução end.change select para incluir fieldid e executá-la.
Sridhar

28
'tempdb..#name'é exatamente o que eu precisava. Eu estava usando 'dbo.#name', como um tolo. Eu recebo o tempdbpapel, mas o que há com os pontos duplos?
usar o seguinte código

77
@ Conrad.Dean double dot é uma abreviação de .dbo.
deutschZuid

32
@deutschZuid é mais preciso dizer que o ponto duplo é o esquema padrão do usuário, que normalmente é dbo (o que não é uma ótima idéia, tornando o dbo o esquema padrão para os usuários, mas geralmente é assim)
jcollum

8
Seu código é tão diferente do OP, que sua declaração 'não é possível reproduzir' não faz sentido. Estou feliz por você ter conseguido que funcionasse de maneira diferente.
precisa

85

A declaração deve ser da ordem

  1. Alterar declaração para a tabela
  2. VAI
  3. Selecionar instrução.

Sem o 'GO' no meio, a coisa toda será considerada como um único script e, quando a instrução select procurar a coluna, ela não será encontrada.

Com 'GO', ele considerará a parte do script até 'GO' como um único lote e será executado antes de entrar na consulta após 'GO'.


7
Isso deve ser marcado como a resposta correta. Não é que o SELECT seja executado antes da tabela de criação; é porque ele está sendo analisado e emitindo um erro antes de ser executado, porque existe uma tabela existente chamada #Results que ainda não tem a coluna FieldId no no momento em que a instrução select é analisada. A adição de um GO separa a consulta em lotes, que são analisados ​​e executados separadamente.
Davos

2
Não acredito na disparidade de votos entre essa e a resposta principal, que mudou tanto o código - sem explicar o porquê - que não teve sentido como resposta.
underscore_d

63

Em vez de droppinge recriar a tabela temporária, você pode truncatee reutilizá-la

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

Se você estiver usando Sql Server 2016ou Azure Sql Databaseuse a sintaxe abaixo para descartar a tabela temporária e recriá-la. Mais informações aqui MSDN

Sintaxe

DROP TABLE [SE EXISTE] [database_name. [nome do esquema]. | schema_name. ] nome_tabela [, ... n]

Inquerir:

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

Parece que o truncate/reusemétodo seria mais eficiente que o DROP TABLE IF EXISTSon Sql Server 2016e Azure Sql Databasetambém. Não é este o caso?
JDawg

@prdp Por que você sugere o DROP TABLE IF ExistsSQL 2016 ou o Azure? A sintaxe está disponível a partir do SQL 2008. Consulte o link MSDN na sua resposta? Fator de desempenho?
HappyTown

4
Deixa pra lá. Agora eu percebi, DROP TABLEé suportada a partir do SQL Server 2008, mas a IF EXISTScláusula foi introduzida em 2016.
HappyTown

1
Eu uso INTO: select * INTO #HistoricoUserTable from dbo.HistoricoUser
Kiquenet

54

Acho que o problema é que você precisa adicionar a instrução GO no meio para separar a execução em lotes. Como o segundo script de descarte, ou seja IF OBJECT_ID('tempdb..#Results') IS NOT NULL DROP TABLE #Results, não descartou a tabela temporária fazendo parte de um único lote. Você pode tentar o script abaixo.

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
De importância; tempdb..no código acima é muito importante. Ele precisa preceder o nome da tabela temporária. Simplesmente verificar OBJECT_ID('#Results')não é suficiente. As tabelas temporárias são armazenadas no banco de dados TempDB. Por Microsoft: o banco de dados do sistema TempDB é um recurso global disponível para todos os usuários conectados à instância do SQL Server ou ao banco de dados SQL
iCode

Obrigado, @iCode. Essa é a chave para eliminar as tabelas temporárias: ela precisa ser executada tempdbou não desaparecerá.
Alex

37

Isso pode ser realizado com uma única linha de código:

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

1
devo olhar para isso todos os dias
Ab Bennett

28

Isso funcionou para mim: 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
Esta é apenas uma sintaxe diferente para a queda condicional da tabela. É interessante, mas não resolve a questão do OP, e a maior parte é redundante. Se você apenas marcar OBJECT_ID (N'tempdb .. # Results ') não é nulo, isso é suficiente para provar que o objeto já existe.
Davos

21

Apenas um pequeno comentário do meu lado, pois o OBJECT_IDnão funciona para mim. Sempre retorna que

`#tempTable não existe

..even embora não existir. Acabei de descobrir que ele é armazenado com um nome diferente (postfixado por _sublinhados) da seguinte forma:

#tempTable________

Isto funciona bem para mim:

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

6
Cuidado: Esse código detectará uma tabela se ela tiver sido criada por qualquer encadeamento. As tabelas # temporárias únicas são criadas separadamente por encadeamento / chamador para um processo armazenado, e é por isso que os sublinhados no nome fazem com que uma cópia diferente exista por encadeamento / processo. O Object_ID deve funcionar bem para o segmento atual, desde que você esteja no SQL 2005 ou posterior.
Bymaster Master

12

Agora você pode usar a sintaxe abaixo se estiver usando uma das novas versões do SQL Server (2016 ou superior).

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

1
Estou usando SSMS 17,3 e isso dáIncorrect syntax near the keyword 'IF'.
StingyJack

7
@StingyJack Porque a sintaxe do SQL não está relacionada à versão do SSMS, mas à versão do SQL Server. A IF [NOT] EXISTScláusula está disponível no SQL Server 2016. Não importa qual versão do SSMS você está usando.
Pref

10

O pmac72 está usando o GO para dividir a consulta em lotes e usando um ALTER.

Você parece estar executando o mesmo lote, mas executando-o duas vezes após alterá-lo: DROP ... CREATE ... edit ... DROP ... CREATE ..

Talvez publique seu código exato para que possamos ver o que está acontecendo.


7

Eu geralmente acerto esse erro quando já criei a tabela temporária; o código que verifica erros na instrução SQL vê a tabela temporária "antiga" no lugar e retorna um número incorreto no número de colunas em instruções posteriores, como se a tabela temporária nunca fosse descartada.

Depois de alterar o número de colunas em uma tabela temporária, depois de criar uma versão com menos colunas, descarte a tabela e, em seguida, execute sua consulta.


6

Vi recentemente um DBA fazer algo semelhante a este:

begin try
    drop table #temp
end try

begin catch 
    print 'table does not exist'
end catch 

create table #temp(a int, b int)

2
Essa instrução try detectaria outros erros que poderiam ocorrer ao tentar soltar a tabela. Esse código pressupõe que a única razão pela qual a tentativa falharia é porque a tabela não existe. Provavelmente funcionaria a maior parte do tempo, mas eu não garanto. Se a instrução try falhar por algum outro motivo, você receberá um erro ao criar a tabela, porque isso ocultou o problema real de descartar a tabela.
Davos

Isso funciona, mas é ruim. Não encorajo da maneira mais difícil quando há uma solução inteligente e perfeita. E também, embora OP especificado versão de 2005, bloco try catch não é suportado em versões mais antigas
dejjub-AIS

O outro problema com isso é a ideologia do uso de try / catch vs logic. Você pode ver mais do debate aqui: stackoverflow.com/questions/17335217/try-catch-or-if-statement/...
logixologist

3

Meu código usa uma Sourcetabela que muda e uma Destinationtabela que deve corresponder a essas alterações.

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

Sim, "coluna inválida" esse erro foi gerado a partir da linha "selecione empresa, stepid, fieldid, NewColumn de #Results".

Existem duas fases de execução do t-sql,

primeiro, analisando, nesta fase, o servidor sql verifica a correção da string sql que você enviou, incluindo a coluna da tabela, e otimizou sua consulta para uma recuperação mais rápida.

segundo, executando, recuperando os dados.

Se a tabela #Results existir, o processo de análise verificará se as colunas especificadas são válidas ou não; caso contrário (a tabela não existe), a análise passará pelas colunas de verificação conforme você especificou.


0

Ao alterar uma coluna em uma tabela temporária, você deve soltar a tabela antes de executar a consulta novamente. (Sim, é irritante. Exatamente o que você precisa fazer.)

Sempre presumi que isso ocorre porque a verificação de "coluna inválida" é feita pelo analisador antes da execução da consulta, portanto, ela se baseia nas colunas da tabela antes de ser descartada ... e foi o que o pnbs também disse.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.