Eu tenho um processo que pega um monte de registros (1000's) e opera neles, e quando eu terminar, eu preciso marcar um grande número deles como processados. Posso indicar isso com uma grande lista de IDs. Estou tentando evitar o padrão "atualizações em um loop", então gostaria de encontrar uma maneira mais eficiente de enviar esse pacote de IDs para um proc armazenado do MS SQL Server 2008.
Proposta nº 1 - Parâmetros com valor de tabela. Eu 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 proc.
Proposta nº 3 - Análise de lista. Prefiro evitar isso, se possível, pois parece pesado e propenso a erros.
Alguma preferência entre estes, ou alguma idéia que eu tenha perdido?
Os melhores artigos de sempre sobre este assunto são de Erland Sommarskog:
Ele cobre todas as opções e explica muito bem.
Desculpe a brevidade da resposta, mas o artigo de Erland sobre Arrays é como os livros de Joe Celko sobre árvores e outros deleites SQL :)
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 . Esta é essencialmente a solução do SQL Server para o seu problema - passando uma lista de valores para um procedimento armazenado.
As vantagens desta abordagem são:
No entanto, observe: Se você chamar um procedimento armazenado que usa TVPs via ADO.NET ou ODBC e observar 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 da chamada para o procedimento. Isso é por design (link quebrado) , ele usa uma inserção eficiente semelhante ao BCP (veja também Dan Guzman sobre isso). 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 eliminam outras abordagens em termos de desempenho e usabilidade para a maioria dos casos de uso.If you want to learn more, Erland Sommarskog has the full skinny on how table-valued parameters work and provides several examples.
Here is another example I concocted:
The entire subject is discussed on the definitive article by Erland Sommarskog: "Arrays and List in SQL Server". Take your pick of which version to choose.
Summary, for pre SQL Server 2008 where TVPs trump the rest
The article is worth reading anyway to see other techniques and thinking.
Edit: late answer for huge lists elsewhere: Passing array parameters to a stored procedure
I know I am late for this party, but I had such a problem in the past, having to send up to 100K bigint numbers, and did a few benchmarks. We ended up sending them in binary format, as an image - that was faster than everything else for up to 100K numbers.
Here is my old (SQL Server 2005) code:
The following code is packing integers into a binary blob. I am reversing the order of bytes here:
Estou dividido entre encaminhá-lo para SO ou respondê-lo aqui, porque isso é quase uma questão de programação. Mas como já tenho uma solução eu uso... vou postar isso ;)
A maneira como isso funciona é que você alimenta uma string delimitada por vírgulas (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 de volta, uma tabela de apenas varchars.
Isso permite que você envie os valores apenas dos ids que deseja processar e pode fazer uma junção simples nesse ponto.
Alternativamente, você pode fazer algo com um CLR DataTable e alimentá-lo, mas isso é um pouco mais de sobrecarga para suportar e todos entendem as listas CSV.
Recebo regularmente conjuntos de milhares de linhas e milhares de linhas enviadas de 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 porquês, pois estão fora do escopo desta solicitação.
Não considerei o processamento de XML, pois não encontrei uma implementação de XML que permaneça com mais de 10.000 "linhas".
O processamento de listas pode ser tratado pelo processamento de tabela de contagem (números) de dimensão única e de dimensão dupla. Nós os usamos com sucesso em várias áreas, mas os TVPs bem gerenciados têm melhor desempenho quando há mais de algumas centenas de "linhas".
Como acontece com todas as opções relacionadas ao processamento do SQL Server, você deve fazer sua seleção com base no modelo de uso.
Finalmente tive a chance de fazer alguns TableValuedParameters e eles funcionam muito bem, então vou colar um monte de código que mostra como estou usando-os, com uma amostra de alguns dos meus códigos atuais: (nota: usamos 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 extraí tudo isso de o aplicativo de console. Desculpe meu estilo de codificação (como strings de conexão codificadas), pois era uma espécie de "construir um para jogar fora". Eu queria mostrar como eu uso a
List<customObject>
e o empurro no 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 ao meu estilo de codificação, se você tiver isso a oferecer (a todos os leitores que se depararem com essa pergunta), mas, por favor, mantenha-as construtivas ;) ... Se você realmente me quer, encontre-me na sala de bate-papo aqui . Espero que com este pedaço de código seja possível ver como eles podem usar o
List<Current>
como eu o defini como uma tabela no banco de dados e umList<T>
em seu aplicativo.Eu iria com a proposta nº 1 ou, como alternativa, criaria uma tabela de rascunho que contém apenas os IDs processados. Insira nessa tabela durante o processamento e, depois de concluído, chame um proc semelhante ao abaixo:
Você fará muitas inserções, mas elas serão em uma mesa pequena, então deve ser rápido. Você também pode agrupar suas inserções usando ADO.net ou qualquer adaptador de dados que esteja usando.
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 isso também.
No contexto do sql-server-2008, conforme especificado pelas tags, há outro ótimo artigo de E. Sommarskog Arrays and Lists in SQL Server 2008 . BTW, encontrei no artigo que Marian se referiu em sua resposta.
Em vez de apenas dar o link, cito sua lista de conteúdo:
Além das técnicas mencionadas lá, tenho a sensação de que em alguns casos bulkcopy e bulk insert merecem ser mencionados no escopo do caso geral.
For MS SQL 2016 latest version
With MS SQL 2016 they introduce a new function : SPLIT_STRING() to parse multiple values.
This can solve your problem easily.
For MS SQL Older Version
If you are using older version, than follow this step:
First Make one function:
After making this, Just pass your string to this function with separator.
I hope this is helpful to you. :-)