Usuário com melhor resposta
Cursor ou variável table?

Pergunta
-
O que é mais performático o cursor ou a variável table?
Para exemplificar montarei uma consulta com a variável table (como geralmente eu faço).
Declare @tabela as table (codigo int, nome varchar(30), usou bit)
Insert Into @tabela (codigo,Nome,Usou) Select Codigo, Nome,0 From Produto
While (Select Count(*) From @tabela Where usou = 0) > 0
Begin
declare @codigo int = 0
select top 1 @codigo = codigo from @tabela where usou = 0
update @tabela set
usou = 1
where codigo = @codigo
End
Bem essa é a estrutura que eu uso para fazer loop. Aceito sugestões de melhorias.
Concatenado
Respostas
-
Boa Tarde,
Normalmente eu não uso nenhum nem outro quando você pode fazer tudo em um comando só. Muitas vezes, os implementadores constroem soluções que pegam um conjunto de registros e lê linha a linha para fazer algo que poderia ser feito de uma vez só. Nessas situações, o uso de tabelas do tipo TABLE e cursores costuma gerar mais código e comporta-se de forma menos performática.
Entretanto, há situações em que isso não é possível, pois, se você tiver que executar uma SP para cada registro então o loop é mesmo necessário (ainda assim a revisão da SP pode trazer diversas melhorias).
Já vi situações onde variáveis do tipo TABLE ganharam e outras que o cursor ganhou (normalmente para muitas linhas). Ainda assim, é importante fazer as coisas de forma performática independente da implementação. Se for usar cursores, coloque o FAST_FORWARD se aplicável. No seu hipotético exemplo com tabelas do tipo TABLE há duas considerações a fazer:
- Retire o COUNT do WHILE. Crie uma variável, calcule a quantidade de linhas uma única vez e use essa variável
- Na variável @tabela use apenas as colunas necessárias. Se você vai usar só a coluna codigo não crie a tabela com a coluna nome
- Utilize uma coluna Identity para efetuar o controle ao invés de uma coluna bit. Isso evita a recontagem múltiplas vezes. Isso é particularmente importante para tabelas com muitos registros já que você não pode criar um índice na variável e cada COUNT(*) seria um SCANSugiro mudar a implementação:
Declare @tabela as table (Seq INT IDENTITY(1,1) PRIMARY KEY, codigo int)
Insert Into @tabela (codigo) Select Codigo From ProdutoDECLARE @TotalLinhas INT, @IntContador INT
SET @TotalLinhas = (SELECT COUNT(*) FROM @Tabela)
SET @IntContador = 0While @IntContador < @TotalLinhas
Begin
--Aqui eu coloco o procedimento que eu desejo realizar para cada item da tabela
SET @IntContador = @IntContador + 1
End[ ]s,
Gustavo Maia Aguiar
Blog: http://gustavomaiaaguiar.wordpress.com
Vídeos: http://www.youtube.com/user/gmasql
Classifique as respostas. O seu feedback é imprescindível- Marcado como Resposta Thiago, Moura segunda-feira, 21 de novembro de 2011 10:26
Todas as Respostas
-
Thiago
Acredito que sua pergunta é referente a Cursor x While
eu diria que qq coisa serializado será mais lento do que algo feito em batch, neste seu exemplo vc esta atualizando o campo usou para 1 onde era =0 sendo assim basta fazer
update
@tabela set usou =1 where usou =
0
Att.
Marcelo Fernandes
MCP, MCDBA, MCSA, MCTS.
Se útil, classifique!!!
Me siga no twitter: @marcelodba -
Não, isso é a minha estrutura de loop que eu utilizo no sql server. Eu monto sempre essa estrutura e insiro o que eu preciso fazer no meio do batch. Esse campo usou é apenas para uma maneira que eu possuo para fazer com que todos os itens da tabela sejam percorridos, entendeu???
Exemplo:
Declare @tabela as table (codigo int, nome varchar(30), usou bit)
Insert Into @tabela (codigo,Nome,Usou) Select Codigo, Nome,0 From Produto
While (Select Count(*) From @tabela Where usou = 0) > 0
Begin
declare @codigo int = 0
select top 1 @codigo = codigo from @tabela where usou = 0
--Aqui eu coloco o procedimento que eu desejo realizar para cada item da tabela
update @tabela set
usou = 1
where codigo = @codigo
End
Concatenado -
Boa Tarde,
Normalmente eu não uso nenhum nem outro quando você pode fazer tudo em um comando só. Muitas vezes, os implementadores constroem soluções que pegam um conjunto de registros e lê linha a linha para fazer algo que poderia ser feito de uma vez só. Nessas situações, o uso de tabelas do tipo TABLE e cursores costuma gerar mais código e comporta-se de forma menos performática.
Entretanto, há situações em que isso não é possível, pois, se você tiver que executar uma SP para cada registro então o loop é mesmo necessário (ainda assim a revisão da SP pode trazer diversas melhorias).
Já vi situações onde variáveis do tipo TABLE ganharam e outras que o cursor ganhou (normalmente para muitas linhas). Ainda assim, é importante fazer as coisas de forma performática independente da implementação. Se for usar cursores, coloque o FAST_FORWARD se aplicável. No seu hipotético exemplo com tabelas do tipo TABLE há duas considerações a fazer:
- Retire o COUNT do WHILE. Crie uma variável, calcule a quantidade de linhas uma única vez e use essa variável
- Na variável @tabela use apenas as colunas necessárias. Se você vai usar só a coluna codigo não crie a tabela com a coluna nome
- Utilize uma coluna Identity para efetuar o controle ao invés de uma coluna bit. Isso evita a recontagem múltiplas vezes. Isso é particularmente importante para tabelas com muitos registros já que você não pode criar um índice na variável e cada COUNT(*) seria um SCANSugiro mudar a implementação:
Declare @tabela as table (Seq INT IDENTITY(1,1) PRIMARY KEY, codigo int)
Insert Into @tabela (codigo) Select Codigo From ProdutoDECLARE @TotalLinhas INT, @IntContador INT
SET @TotalLinhas = (SELECT COUNT(*) FROM @Tabela)
SET @IntContador = 0While @IntContador < @TotalLinhas
Begin
--Aqui eu coloco o procedimento que eu desejo realizar para cada item da tabela
SET @IntContador = @IntContador + 1
End[ ]s,
Gustavo Maia Aguiar
Blog: http://gustavomaiaaguiar.wordpress.com
Vídeos: http://www.youtube.com/user/gmasql
Classifique as respostas. O seu feedback é imprescindível- Marcado como Resposta Thiago, Moura segunda-feira, 21 de novembro de 2011 10:26