SELECIONE EM uma variável de tabela no T-SQL

372

Recebi uma consulta SELECT complexa, da qual gostaria de inserir todas as linhas em uma variável de tabela, mas o T-SQL não permite.

Na mesma linha, você não pode usar uma variável de tabela com as consultas SELECT INTO ou INSERT EXEC. http://odetocode.com/Articles/365.aspx

Breve exemplo:

declare @userData TABLE(
                        name varchar(30) NOT NULL,
                        oldlocation varchar(30) NOT NULL
                       )

SELECT name, location
INTO @userData
FROM myTable
    INNER JOIN otherTable ON ...
WHERE age > 30

Os dados na variável da tabela seriam usados ​​posteriormente para inseri-los / atualizá-los novamente em tabelas diferentes (principalmente cópia dos mesmos dados com pequenas atualizações). O objetivo disso seria simplesmente tornar o script um pouco mais legível e mais facilmente personalizável do que fazer SELECT INTOdiretamente nas tabelas corretas. O desempenho não é um problema, pois rowcounté pequeno e só é executado manualmente quando necessário.
... ou apenas me diga se estou fazendo tudo errado.

Indrek
fonte

Respostas:

601

Tente algo como isto:

DECLARE @userData TABLE(
    name varchar(30) NOT NULL,
    oldlocation varchar(30) NOT NULL
);

INSERT INTO @userData (name, oldlocation)
SELECT name, location FROM myTable
INNER JOIN otherTable ON ...
WHERE age > 30;
CristiC
fonte
2
Se você "SELECT name, location FROM myTable" como os valores que serão inseridos na tabela UserData, não importa se os nomes das variáveis ​​na seleção correspondem aos nomes na definição da tabela. Você está selecionando 'name' para acessar a variável UserData 'name', mas está selecionando 'location' e, de alguma forma, atribuindo-o à variável 'oldlocation' do UserData. O SQL apenas mapeia esses dados automaticamente ou gera algum tipo de exceção?
27615 Aran
Não importa o nome, apenas o tipo de coluna.
CristiC 18/11/2015
5
Uau, que tipo de faz sentido, mas ao mesmo tempo o analisador em mim sente tipo de ofendido :)
Aran Mulholland
Eu não consigo ser capaz de usar isso em declaração UPDATE (s): ligação essência
Paul-Sebastian Manole
11
Em uma instrução de inserção, se você não declarar as colunas explicitamente, elas serão mapeadas na ordem declarada na instrução de criação de tabela original, assim como selecionar *. Portanto, o local na instrução select é mapeado para oldlocation na tabela @userData porque o local está na posição 2 no conjunto de resultados do select e oldlocation é a coluna 2 na definição da tabela. Dito isto, nunca faça isso. A ordenação de banco de dados de colunas ou linhas não é confiável. Seja sempre explícito sobre isso.
absmiths
94

O objetivo de SELECT INTOé (de acordo com os documentos, minha ênfase)

Para criar uma nova tabela a partir dos valores em outra tabela

Mas você já tem uma tabela de destino! Então o que você quer é

A INSERTinstrução adiciona uma ou mais novas linhas a uma tabela

Você pode especificar os valores dos dados das seguintes maneiras:

...

Usando uma SELECTsubconsulta para especificar os valores dos dados para uma ou mais linhas, como:

  INSERT INTO MyTable 
 (PriKey, Description)
        SELECT ForeignKey, Description
        FROM SomeView

E nessa sintaxe, é permitido MyTableque seja uma variável de tabela.

AakashM
fonte
11
Realmente desejo que a resposta aceita inclua esta informação!
Davie Brown
Recebo MyTable é "Nome de objeto inválido" fazendo isso, então há algo faltando nesta resposta.
Mike Flynn
@ MikeFlynn MyTableaqui é um espaço reservado para o nome da sua tabela real . Eu não acho que existem quaisquer bases de dados reais com uma tabela chamada MyTable...
AakashM
E se eu quiser criar / declarar uma variável de tabela com SELECT INTO ...? Por exemplo, para definir as colunas da variável da tabela como t1.somecolumn, t1.othercolumn, t2. *
Armando
27

Você também pode usar expressões de tabela comuns para armazenar conjuntos de dados temporários. Eles são mais elegantes e amigáveis:

WITH userData (name, oldlocation)
AS
(
  SELECT name, location 
  FROM   myTable    INNER JOIN 
         otherTable ON ...
  WHERE  age>30
)
SELECT * 
FROM   userData -- you can also reuse the recordset in subqueries and joins
nanestev
fonte
Amo isso! Obrigado.
Fourpastmidnight
Eu não acho que isso faça uma cópia, se você excluir ou atualizar do userData, ele não excluirá nem atualizará os registros em suas tabelas originais?
Atreeon 12/04/19
Sim, DELETE e UPDATE na CTE vai modificar a tabela de origem, desde que o CTE não faz referência a várias tabelas usando associações, sindicatos, etc.
nanestev
2
A desvantagem disso é que você só pode usar a tabela CTE nos seguintes comandos imediatamente. Se você precisar fazer mais de uma passagem no conjunto de resultados por qualquer motivo, o CTE não funcionará. O OP parece sugerir que várias modificações serão feitas; nesse caso, isso não funcionará - "Os dados na variável da tabela seriam usados ​​posteriormente para inseri-los / atualizá-los novamente em tabelas diferentes (na maior parte cópia dos mesmos dados com pequenas alterações). atualizações). "
Tony
16

Você pode tentar usar tabelas temporárias ... se não estiver fazendo isso a partir de um aplicativo. (Pode ser bom executar isso manualmente)

SELECT name, location INTO #userData FROM myTable
INNER JOIN otherTable ON ...
WHERE age>30

Você pula o esforço para declarar a tabela dessa maneira ... Ajuda para consultas adhoc ... Isso cria uma tabela temporária local que não será visível para outras sessões, a menos que você esteja na mesma sessão. Talvez seja um problema se você estiver executando uma consulta em um aplicativo.

se você precisar que ele seja executado em um aplicativo, use variáveis ​​declaradas desta maneira:

DECLARE @userData TABLE(
    name varchar(30) NOT NULL,
    oldlocation varchar(30) NOT NULL
);

INSERT INTO @userData
SELECT name, location FROM myTable
INNER JOIN otherTable ON ...
WHERE age > 30;

Editar: muitos de vocês mencionaram visibilidade atualizada para a sessão da conexão. Criar tabelas temporárias não é uma opção para aplicativos da Web, pois as sessões podem ser reutilizadas, atenha-se às variáveis ​​temporárias nesses casos

Mulki
fonte
2
Desculpe, esqueci de mencionar que não tenho direitos para CREATE TABLE.
Indrek
6
Criar uma temperatura tem um pouco mais de sobrecarga.
Paparazzo
2
o uso da tabela temporária nem sempre é seguro. Por exemplo, serviços da web. Com os serviços da web com uma única conexão para limitar a conexão máxima no servidor e proteger o SQL um pouco mais, a tabela temporária existirá para TODAS as consultas de passagem e pode substituir alguém atualmente usando.
Franck
12
@ Frank - se você usar uma tabela temporária global (prefixo de dois hash), estará correta. No entanto, uma tabela temporária local (um prefixo de hash) será isolada em uma única sessão (também conhecida como conexão única), para que não haja problemas de simultaneidade aos quais você está se referindo, a menos que esteja usando uma conexão única para todas as solicitações (não aconselhado). As possíveis implicações de desempenho permanecem, no entanto.
maf748
@GazB Claro, qualquer declaração com efeito colateral é excluída de ser usada em a function. Na minha experiência, na maioria dos casos em que alguém pensa que precisa de tais declarações, isso realmente significa que deve repensar sua function- ou pelo menos refatorar para a procedure. Falando por mim mesmo, pelo menos. :-)
underscore_d
8

Tente usar em INSERTvez de SELECT INTO:

INSERT @UserData   
SELECT name, location etc.
Noel Abrahams
fonte
5

Primeiro, crie uma tabela temporária:

Passo 1:

create table #tblOm_Temp (

    Name varchar(100),
    Age Int ,
    RollNumber bigint
)

** Etapa 2: ** Insira algum valor na tabela Temp.

insert into #tblom_temp values('Om Pandey',102,1347)

Etapa 3: Declarar uma variável da tabela para armazenar dados da tabela temporária.

declare   @tblOm_Variable table(

    Name Varchar(100),
    Age int,
    RollNumber bigint
)

Etapa 4: selecione o valor da tabela temporária e insira na variável da tabela.

insert into @tblOm_Variable select * from #tblom_temp

Finalmente, o valor é inserido de uma tabela temporária na variável Table

Etapa 5: é possível verificar o valor inserido na variável da tabela.

select * from @tblOm_Variable
404 não encontrado
fonte
1

OK, agora com bastante esforço eu sou capaz de inserir na @table usando o seguinte:

INSERT @TempWithheldTable SELECT
a.SuspendedReason, a.SuspendedNotes, a.SuspendedBy, a.ReasonCode FROM OPENROWSET (GRUPO 'C: \ DataBases \ WithHeld.csv', FORMATFILE = N'C: \ DataBases \ Format.txt ',
ERRORFILE = N'C: \ Temp \ MovieLensRatings.txt ') AS a;

O principal aqui é selecionar colunas para inserir.

RahulJha
fonte
Eu recebo um 'Deve declarar a variável de tabela mensagem '@TempWithheldTable' Erro de compilação
atreeon
-5

Uma razão para usar SELECT INTO é que ele permite que você use IDENTITY:

SELECT IDENTITY(INT,1,1) AS Id, name
INTO #MyTable 
FROM (SELECT name FROM AnotherTable) AS t

Isso não funcionaria com uma variável de tabela, o que é muito ruim ...

MOHCTP
fonte
6
Você pode declarar uma variável de tabela com uma IDENTITYcoluna.
Martin Smith