Eu tenho um processo que pega um monte de registros (milhares) e os opera, e quando terminar, preciso marcar um grande número deles como processados. Eu posso indicar isso com uma grande lista de IDs. Estou tentando evitar o padrão "atualizações em loop", então gostaria de encontrar uma maneira mais eficiente de enviar esse pacote de IDs para um processo armazenado no MS SQL Server 2008.
Proposta nº 1 - Parâmetros com valor de tabela. Posso definir um tipo de tabela com apenas um campo de ID e enviar uma tabela cheia de IDs para atualizar.
Proposta 2 - Parâmetro XML (varchar) com OPENXML () no corpo do processo.
Proposta nº 3 - análise de lista. Prefiro evitar isso, se possível, pois parece difícil e propenso a erros.
Alguma preferência entre essas ou alguma idéia que eu perdi?
fonte
Respostas:
Os melhores artigos de sempre sobre esse assunto são de Erland Sommarskog:
Ele cobre todas as opções e explica muito bem.
Desculpe a falta de resposta, mas o artigo de Erland sobre Arrays é como os livros de Joe Celko sobre árvores e outras guloseimas do SQL :)
fonte
Há uma grande discussão sobre isso no StackOverflow, que abrange muitas abordagens. O que eu prefiro para o SQL Server 2008+ é usar parâmetros com valor de tabela . Essa é essencialmente a solução do SQL Server para o seu problema - passando uma lista de valores para um procedimento armazenado.
As vantagens dessa abordagem são:
No entanto, observe: Se você chamar um procedimento armazenado que usa TVPs via ADO.NET ou ODBC e examinar a atividade com o SQL Server Profiler, notará que o SQL Server recebe várias
INSERT
instruções para carregar o TVP, uma para cada linha no TVP , seguido pela chamada para o procedimento. Isso ocorre por design . Esse lote deINSERT
s precisa ser compilado toda vez que o procedimento é chamado e constitui uma pequena sobrecarga. No entanto, mesmo com essa sobrecarga, os TVPs ainda descartam outras abordagens em termos de desempenho e usabilidade para a maioria dos casos de uso.Se você quiser saber mais, Erland Sommarskog tem o skinny cheio em como parâmetros com valor de tabela trabalhar e fornece vários exemplos.
Aqui está outro exemplo que inventei:
fonte
CREATE TYPE
declaração no início foi executada com sucesso? Qual versão do SQL Server você está executando?@customer_list
não é@param1
. O exemplo simplesmente demonstra que você pode misturar diferentes tipos de parâmetros.Todo o assunto é discutido no artigo definitiva por Erland Sommarskog: "Matrizes e lista em SQL Server" . Escolha qual versão escolher.
Resumo, para pré SQL Server 2008 onde TVPs trunfo o resto
De qualquer maneira, vale a pena ler o artigo para ver outras técnicas e pensamentos.
Edit: resposta tardia para grandes listas em outros lugares: Passando parâmetros de matriz para um procedimento armazenado
fonte
Sei que estou atrasado para esta festa, mas tive um problema no passado, tendo que enviar até 100 mil números grandes e fiz alguns benchmarks. Acabamos enviando-os em formato binário, como uma imagem - que era mais rápida do que tudo o resto para números de até 100K.
Aqui está o meu código antigo (SQL Server 2005):
O código a seguir está compactando números inteiros em um blob binário. Estou revertendo a ordem dos bytes aqui:
fonte
Estou dividido entre referir você a SO ou responder aqui, porque essa é quase uma questão de programação. Mas como já tenho uma solução que uso ... vou postar isso;)
A maneira como isso funciona é que você alimenta uma string delimitada por vírgula (divisão simples, não faz divisões no estilo CSV) no procedimento armazenado como um varchar (4000) e, em seguida, alimenta essa lista nessa função e obtém uma tabela útil novamente, uma tabela de apenas varchars.
Isso permite que você envie os valores apenas dos IDs que deseja processar, e você pode fazer uma associação simples nesse ponto.
Como alternativa, você pode fazer algo com uma CLT DataTable e alimentá-lo, mas isso é um pouco mais caro para dar suporte e todo mundo entende as listas CSV.
fonte
Eu recebo regularmente conjuntos de milhares de linhas e 10000 linhas enviadas do nosso aplicativo para serem processadas por vários procedimentos armazenados do SQL Server.
Para atender às demandas de desempenho, usamos TVPs, mas você deve implementar seu próprio resumo do dbDataReader para superar alguns problemas de desempenho em seu modo padrão de processamento. Não vou entrar nos comos e nos porquês, pois estão fora do escopo desta solicitação.
Não considerei o processamento XML, pois não encontrei uma implementação XML com desempenho superior a 10.000 "linhas".
O processamento de lista pode ser tratado pelo processamento de tabela de contagem unidimensional e dupla dimensão (números). Nós os usamos com sucesso em várias áreas, mas os TVPs bem gerenciados têm melhor desempenho quando existem mais de algumas centenas de "linhas".
Como em todas as opções relacionadas ao processamento do SQL Server, você deve fazer sua seleção com base no modelo de uso.
fonte
Finalmente tive a chance de fazer alguns TableValuedParameters e eles funcionam muito bem, então vou colar um código inteiro que mostra como os estou usando, com uma amostra de alguns dos meus códigos atuais: (note: we use ADO .INTERNET)
Observe também: estou escrevendo algum código para um serviço e tenho muitos bits de código predefinidos na outra classe, mas estou escrevendo isso como um aplicativo de console para que eu possa depurá-lo, então retirei tudo isso de o aplicativo do console. Desculpe meu estilo de codificação (como cadeias de conexão codificadas), pois era como "criar um para jogar fora". Eu queria mostrar como uso um
List<customObject>
e enviá-lo ao banco de dados facilmente como uma tabela, que posso usar no procedimento armazenado. Código C # e TSQL abaixo:Além disso, aceitarei críticas construtivas sobre o meu estilo de codificação, se você tiver isso a oferecer (a todos os leitores que se depararem com essa pergunta), mas mantenha-a construtiva;) ... Se você realmente me quiser, encontre-me na sala de bate-papo aqui . Felizmente, com esse pedaço de código, é possível ver como eles podem usar o
List<Current>
que eu defini como uma tabela no banco de dados e umList<T>
no aplicativo.fonte
Eu aceitaria a proposta nº 1 ou, como alternativa, criaria uma tabela de rascunho que contém apenas os IDs processados. Insira essa tabela durante o processamento e, depois de concluído, chame um processo semelhante ao abaixo:
Você fará muitas inserções, mas elas serão para uma mesa pequena, portanto deve ser rápido. Você também pode colocar em lote suas inserções usando o ADO.net ou qualquer adaptador de dados que esteja usando.
fonte
O título da pergunta inclui a tarefa de transmitir dados de um aplicativo para o procedimento armazenado. Essa parte é excluída pelo corpo da pergunta, mas deixe-me tentar responder a isso também.
No contexto do sql-server-2008, conforme especificado pelas tags, há outro ótimo artigo de E. Sommarskog Arrays and Lists no SQL Server 2008 . Aliás, encontrei no artigo que Marian mencionou em sua resposta.
Em vez de apenas fornecer o link, cito sua lista de conteúdo:
Além das técnicas mencionadas, tenho a sensação de que, em alguns casos, a cópia em massa e a inserção em massa merecem ser mencionadas no escopo do caso geral.
fonte
Para a versão mais recente do MS SQL 2016
Com o MS SQL 2016, eles introduzem uma nova função: SPLIT_STRING () para analisar vários valores.
Isso pode resolver seu problema facilmente.
Para versão anterior do MS SQL
Se você estiver usando uma versão mais antiga, siga esta etapa:
Primeiro faça uma função:
Depois de fazer isso, basta passar sua string para esta função com separador.
Espero que isso seja útil para você. :-)
fonte
Use isso para criar "criar tabela de tipos". exemplo simples para usuário
fonte