Quando usar a instrução SELECT… INTO (PE003)
Podemos usar SELECT…INTO
no SQL Server para criar uma nova tabela a partir de uma fonte de tabela. O SQL Server usa os atributos das expressões na lista SELECT
para definir a estrutura da nova tabela.
Antes do SQL Server 2005, o uso de SELECT…INTO
no código de produção era um ‘cheiro de código’ de desempenho porque adquiria bloqueios de esquema nas tabelas do sistema do banco de dados, fazendo com que o SQL Server parecesse sem resposta enquanto a consulta estava sendo executada. Isso ocorre porque é uma instrução DDL em uma transação implícita, que é inevitavelmente longa, pois os dados são inseridos na mesma instrução SQL. No entanto, esse comportamento foi corrigido no SQL Server 2005, quando o modelo de bloqueio mudou.
SELECT…INTO
tornou-se popular porque era uma maneira mais rápida de inserir dados do que usar INSERT
INTO…SELECT…
. Isso se deveu principalmente ao fato de a operação SELECT…INTO
ser, onde possível, registrada em massa. Embora INSERT
INTO
agora possa ser registrado em massa, você ainda pode ver essa vantagem de desempenho no SQL Server 2012 e 2014, porque SELECT…INTO
pode ser paralelizado nessas versões, enquanto o suporte para paralelização de INSERT
INTO
surgiu apenas no SQL Server 2016. No entanto, com SELECT…INTO
, você ainda tem a tarefa de definir todos os índices e restrições necessários, e assim por diante, na nova tabela.
Uma recomendação para evite o uso de SELECT…INTO
, para código de produção, está incluído como uma regra de análise de código no prompt SQL (PE003).
Criação de tabelas usando a instrução SELECT INTO
O recurso SELECT…INTO
no SQL Server foi projetado para armazenar ou ‘persistir’ uma fonte de tabela como parte de um processo. Aqui está um exemplo simples:
No entanto, uma fonte de tabela pode ser muito diferente de uma tabela convencional, como uma função definida pelo usuário, um OpenQuery
, uma OpenDataSource
, uma OPENXML
cláusula, uma tabela derivada, uma tabela combinada, uma tabela dinâmica, uma fonte de dados remota, uma variável de tabela ou uma função variável. É com essas fontes de tabela mais exóticas que a sintaxe SELECT…INTO
se torna mais útil.
É SELECT INTO parte do ANSI padrão?
Os padrões ANSI suportam uma construção SELECT…INTO
; é chamado de seleção de singleton e carrega uma única linha com valores, mas é muito raramente usado (graças a Joe Celko por apontar isso).
As pessoas costumam usar SELECT…INTO
com o mal-entendido de que é uma maneira rápida de copiar tabelas e, portanto, é uma surpresa que nenhum dos índices, restrições, colunas calculadas ou acionadores definidos em a tabela de origem é transferida para a nova tabela. Eles também não podem ser especificados na instrução SELECT…INTO
. Ele também não faz nada sobre a nulidade ou preservação de colunas computadas. Todas essas tarefas devem ser feitas retrospectivamente com os dados no lugar, o que inevitavelmente leva tempo.
Você pode, no entanto, usar a função IDENTITY
(tipo de dados, semente, incremento) para configurar um campo de identidade, e é possível, quando a origem for uma única tabela, transformar uma coluna da tabela de destino em uma coluna de identidade. É esse fato que provavelmente leva os desenvolvedores a presumir que ele irá transferir outros atributos de coluna.
Além disso, ele também não pode criar tabelas particionadas, colunas esparsas ou qualquer outro atributo herdado de uma tabela de origem. Como poderia, quando os dados podem vir de uma consulta envolvendo muitas junções ou de alguma fonte de dados externa exótica?
Desde o SQL 2012 SP1 CU10, SELECT…INTO
pode ser executado em paralelo, no entanto, desde o SQL Server 2016, a inserção paralela foi permitida na instrução INSERT INTO…SELECT
convencional, com certas restrições, portanto, qualquer vantagem de desempenho de usar SELECT
… INTO
agora está bastante reduzido. O INSERT
INTO
processo também pode ser acelerado se puder ser registrado em massa, em vez de totalmente recuperado, definindo o modelo de recuperação para registro simples ou em massa, inserindo em uma tabela vazia ou heap e definindo a TABLOCK
dica para a tabela.
O seguinte resume algumas das restrições e limitações ao usar SELECT…INTO
.
- A propriedade
IDENTITY
de uma coluna é transferida, mas não se:- A instrução
SELECT
contém tabelas unidas (usandoJOIN
ouUNION
),GROUP
BY
cláusula ou função agregada.Se você precisa evitar que uma propriedadeIDENTITY
seja transportada para a nova tabela, mas precisa dos valores da coluna, vale a pena adicionar umJOIN
para sua tabela de origem em uma condição que nunca é verdadeira ou umUNION
que não fornece linhas. - O
IDENTITY
a coluna é listada mais de uma vez naSELECT
lista - A coluna
IDENTITY
faz parte de uma expressão - A coluna
IDENTITY
é de uma fonte de dados remota
- A instrução
- Você não pode
SELECT…INTO
um parâmetro com valor de tabela ou uma variável de tabela como destino, embora você possa selecioná-losFROM
. - Mesmo que sua origem é uma tabela particionada, a nova tabela é criada no grupo de arquivos padrão. No entanto, no SQL Server 2017, é possível especificar o grupo de arquivos no qual a tabela de destino é criada, por meio da cláusula
ON
. - Você pode especificar um
ORDER
BY
cláusula, mas geralmente é ignorada. Por causa disso, a ordem deIDENTITY_INSERT
não é garantida. - Quando uma coluna computada é incluída em
SELECT
lista, a coluna correspondente na nova tabela não é uma coluna computada. Os valores na nova coluna são os valores que foram calculados no momento em queSELECT…INTO
foi executado. - Como com um
CREATE
TABLE
declaração, se umaSELECT…INTO
declaração estiver contida em uma transação explícita, as linhas subjacentes nas tabelas de sistema afetadas são bloqueado exclusivamente até que a transação seja explicitamente confirmada. Enquanto isso, isso resultará em bloqueios em outros processos que usam essas tabelas do sistema.
Há alguma confusão sobre os problemas que podem acontecer com o uso de SELECT…INTO
usando tabelas temporárias. SELECT…INTO
ganhou uma reputação um tanto injusta por isso, mas era parte de um problema mais geral envolvendo contenção de trava em tempdb
sob uma carga pesada de criação e exclusão de pequenas tabelas temporárias. Quando SELECT…INTO
foi adotado com entusiasmo, pode aumentar muito este tipo de atividade. O problema pode ser facilmente corrigido no SQL Server 2000 em diante com a introdução do uso do sinalizador de rastreamento TF1118, que não é mais necessário do SQL Server 2016 em diante. Para uma explicação completa, veja Equívocos em torno do TF 1118.
Resumo
Em resumo, SELECT…INTO
é uma boa maneira de fazer uma tabela- fonte temporariamente persistente como parte de um processo, se você não se preocupa com restrições, índices ou colunas especiais. Não é uma boa maneira de copiar uma tabela porque apenas os fundamentos básicos do esquema da tabela podem ser copiados. Ao longo dos anos, houve fatores que aumentaram ou diminuíram a atração de SELECT…INTO
, mas no geral é uma boa ideia evitar usá-lo sempre que possível. Em vez disso, crie uma tabela explicitamente, com toda a gama de recursos que a tabela possui, projetados para garantir que os dados sejam consistentes.