Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Problemas com o desempenho do parâmetro de valor de tabela


Se os TVPs forem "visivelmente mais lentos" do que as outras opções, provavelmente você não os está implementando corretamente.
  1. Você não deve usar uma DataTable, a menos que seu aplicativo tenha uso para ela fora do envio de valores para o TVP. Usando o IEnumerable<SqlDataRecord> interface é mais rápida e usa menos memória, pois você não está duplicando a coleção na memória apenas para enviá-la ao banco de dados. Eu tenho isso documentado nos seguintes lugares:

  2. Você não deve usar AddWithValue para o SqlParameter, embora isso provavelmente não seja um problema de desempenho. Mas ainda assim, deve ser:
    SqlParameter tvp = com.Parameters.Add("data", SqlDbType.Structured);
    tvp.Value = MethodThatReturnsIEnumerable<SqlDataRecord>(MyCollection);
    
  3. TVPs são variáveis ​​de tabela e, como tal, não mantêm estatísticas. Ou seja, eles relatam ter apenas 1 linha para o Otimizador de consultas. Então, no seu proc, também:
    • Use a recompilação em nível de instrução em qualquer consulta usando o TVP para qualquer coisa que não seja um simples SELECT:OPTION (RECOMPILE)
    • Crie uma tabela temporária local (ou seja, # único ) e copie o conteúdo do TVP na tabela temporária
    • Você pode tentar adicionar uma chave primária agrupada ao tipo de tabela definida pelo usuário
    • Se estiver usando o SQL Server 2014 ou mais recente, tente usar OLTP na memória/tabelas com otimização de memória. Consulte:Tabela temporária e variável de tabela mais rápidas usando otimização de memória

Sobre o motivo pelo qual você está vendo:
insert into @data ( ... fields ... ) values ( ... values ... )
-- for each row
insert into @data ( ... fields ... ) values ( ... values ... )

ao invés de:
insert into @data ( ... fields ... ) 
values ( ... values ... ),
       ( ... values ... ),

SE isso é realmente o que está acontecendo, então:
  • Se as inserções estiverem sendo feitas em uma transação, não haverá diferença real de desempenho
  • A sintaxe de lista de valores mais recente (ou seja, VALUES (row1), (row2), (row3) ) é limitado a algo como 1.000 linhas e, portanto, não é uma opção viável para TVPs que não possuem esse limite. NO ENTANTO, este não é provavelmente o motivo pelo qual inserções individuais estão sendo usadas, dado que não há limite ao fazer INSERT INTO @data (fields) SELECT tab.[col] FROM (VALUES (), (), ...) tab([col]) , que documentei aqui:Número máximo de linhas para o construtor de valor de tabela . Em vez disso...
  • O motivo mais provável é que fazer inserções individuais permite transmitir os valores do código do aplicativo para o SQL Server:
    1. usando um iterador (ou seja, o IEnumerable<SqlDataRecord> observado em #1 acima), o código do aplicativo envia cada linha conforme é retornada do método e
    2. construindo os VALUES (), (), ... lista, mesmo fazendo o INSERT INTO ... SELECT FROM (VALUES ...) abordagem (que não está limitada a 1.000 linhas), que ainda exigiria a construção de inteiro VALUES lista antes de enviar qualquer dos dados no SQL Server. Se houver muitos dados, isso levaria mais tempo para construir a string superlonga e consumiria muito mais memória ao fazê-lo.

Consulte também este whitepaper do SQL Server Customer Advisory Team:Maximizando a taxa de transferência com TVP