Autor convidado:Derik Hammer (@SQLHammer)
Recentemente, Aaron Bertrand blogou sobre mitos prejudiciais e pervasivos sobre o desempenho do SQL Server. Como extensão desta série de blogs, vou refutar esse mito comum:
"As variáveis de tabela estão sempre na memória, portanto, mais rápidas que as tabelas temporárias."
Ler o manual
Indo direto para a fonte, consultei o artigo Books Online sobre tabelas que inclui variáveis de tabela. Embora o artigo faça referência aos benefícios do uso de variáveis de tabela, o fato de elas estarem 100% na memória está claramente ausente.
Uma afirmativa ausente não implica uma negativa, no entanto. Desde que as tabelas OLTP na memória foram lançadas, agora há muito mais documentação em BOL para processamento na memória. Foi aí que encontrei este artigo sobre como tornar as variáveis de tabela e tabela temporárias mais rápidas usando a otimização de memória.
O artigo inteiro gira em torno de como fazer com que seus objetos temporários usem o recurso OLTP na memória, e foi aí que encontrei a afirmativa que estava procurando.
"Uma variável de tabela tradicional representa uma tabela no banco de dados tempdb. Para um desempenho muito mais rápido, você pode otimizar a memória de sua variável de tabela."
As variáveis de tabela não são construções na memória. Para usar a tecnologia in-memory, você precisa definir explicitamente um TYPE que seja otimizado para memória e usar esse TYPE para definir sua variável de tabela.
Prove
Documentação é uma coisa, mas ver com meus próprios olhos é outra bem diferente. Eu sei que as tabelas temporárias criam objetos no tempdb e gravam dados no disco. Primeiro, mostrarei como são as tabelas temporárias e, em seguida, usarei o mesmo método para validar a hipótese de que as variáveis da tabela agem da mesma maneira.
Análise de registro de log
Essa consulta executará um CHECKPOINT para me fornecer um ponto de partida limpo e, em seguida, mostrará o número de registros de log e os nomes de transação que existem no log.
USE tempdb; GO CHECKPOINT; GO SELECT COUNT(*) [Count] FROM sys.fn_dblog (NULL, NULL); SELECT [Transaction Name] FROM sys.fn_dblog (NULL, NULL) WHERE [Transaction Name] IS NOT NULL;
Executar o T-SQL repetidamente resultou em uma contagem consistente de três registros no SQL Server 2016 SP1.
Isso cria uma tabela temporária e exibe o registro do objeto, provando que este é um objeto real no tempdb.
USE tempdb; GO DROP TABLE IF EXISTS #tmp; GO CREATE TABLE #tmp (id int NULL); SELECT name FROM sys.objects o WHERE is_ms_shipped = 0;
Agora vou mostrar os registros de log novamente. Não vou executar novamente o comando CHECKPOINT.
Vinte e um registros de log foram gravados, provando que são gravações em disco, e nosso CREATE TABLE está claramente incluído nesses registros de log.
Para comparar esses resultados com as variáveis de tabela, redefinirei o experimento executando CHECKPOINT e, em seguida, executando o T-SQL abaixo, criando uma variável de tabela.
USE tempdb; GO DECLARE @var TABLE (id int NULL); SELECT name FROM sys.objects o WHERE is_ms_shipped = 0;
Mais uma vez temos um novo registro de objeto. Desta vez, no entanto, o nome é mais aleatório do que com tabelas temporárias.
Há oitenta e dois novos registros de log e nomes de transação provando que minha variável está sendo gravada no log e, portanto, no disco.
Na verdade, na memória
Agora é hora de eu fazer os registros de log desaparecerem.
Criei um grupo de arquivos OLTP na memória e, em seguida, criei um tipo de tabela com otimização de memória.
USE Test; GO CREATE TYPE dbo.inMemoryTableType AS TABLE ( id INT NULL INDEX ix1 ) WITH (MEMORY_OPTIMIZED = ON); GO
Executei o CHECKPOINT novamente e criei a tabela otimizada de memória.
USE Test; GO DECLARE @var dbo.inMemoryTableType; INSERT INTO @var (id) VALUES (1) SELECT * from @var; GO
Depois de revisar o log, não vi nenhuma atividade de log. Este método é de fato 100% na memória.
Retirada
As variáveis de tabela usam tempdb de forma semelhante a como as tabelas temporárias usam tempdb. As variáveis de tabela não são construções na memória, mas podem se tornar elas se você usar tipos de tabela definidos pelo usuário com otimização de memória. Muitas vezes, acho que as tabelas temporárias são uma escolha muito melhor do que as variáveis de tabela. A principal razão para isso é porque as variáveis de tabela não têm estatísticas e, dependendo da versão e das configurações do SQL Server, as estimativas de linha são de 1 ou 100 linhas. Em ambos os casos, essas são suposições e se tornam peças prejudiciais de desinformação em seu processo de otimização de consulta.
Observe que algumas dessas diferenças de recursos podem mudar com o tempo – por exemplo, em versões recentes do SQL Server, você pode criar índices adicionais em uma variável de tabela usando a sintaxe de índice embutido. A tabela a seguir tem três índices; a chave primária (agrupada por padrão) e dois índices não agrupados:
DECLARE @t TABLE ( a int PRIMARY KEY, b int, INDEX x (b, a DESC), INDEX y (b DESC, a) );
Há uma ótima resposta no DBA Stack Exchange, onde Martin Smith detalha exaustivamente as diferenças entre variáveis de tabela e tabelas #temp:
- Qual é a diferença entre uma tabela temporária e uma variável de tabela no SQL Server?