Casos do Dia a Dia – Cuidado ao utilizar o comando SET ROWCOUNT

Olá Mundo,

Nesse post vou falar de mais um Caso do Dia a Dia sobre como o comando SET ROWCOUNT pode gerar alguns problemas e retornar um resultado inconsistente.

Mas antes, deixo aqui um pequeno desafio:

“Quantas linhas serão retornadas no script abaixo?”

OBS: Pense na resposta antes de rodar o script, não vale roubar hein.

A) 10, 10

B) 5, 10

C) 10, 5

D) 5, 5

E) Não sei nem chutar, vou ler o post primeiro e depois respondo

No final do post eu darei a resposta, mas antes vamos ver alguns conceitos e exemplos. #gogogo


SET ROWCOUNT – Conceitos:

O comando SET ROWCOUNT é utilizado para fazer com que o SQL Server pare o processamento da query quando um número específico de registros for retornado.

Referência: https://docs.microsoft.com/pt-br/sql/t-sql/statements/set-rowcount-transact-sql?view=sql-server-ver15

O valor default é 0, mas na verdade isso significa que o SQL Server irá retornar todos os registros.

Vamos ver um exemplo bem simples para entender esse conceito. Vou criar uma tabela e inserir 10 registros. Depois vou utilizar o SET ROWCOUNT 5 e vamos ver o resultado.

O SQL Server retornou apenas 5 registros, sendo que a tabela possui 10 no total. Repare que não utilizamos nenhum filtro no SELECT.

Agora vamos voltar o ROWCOUNT para o valor default e executar o SELECT novamente.

Agora sim o SQL Server retornou todos os registros da tabela. Com isso, acredito que você tenha entendido o funcionamento do ROWCOUNT.

Observação Importante:

A própria Microsoft recomenda que você evite utilizar o SET ROWCOUNT, pois em alguma versão futura do SQL Server ele pode ter o comportamento alterado e recomenda que você modifique as aplicações que utilizam isso. Nesse caso, ela também indica a utilização do comando TOP que tem um comportamento similar.

Ou seja, se você utiliza o comando SET ROWCOUNT nas suas aplicações, você pode ter algum problema de inconsistência no resultado no futuro caso esse comportamento seja alterado. Se eu fosse você, planejaria uma alteração nisso o quanto antes para não ter dor de cabeça depois. #ficaadica

Deprecated Database Engine Features in SQL Server 2016:

Apenas reforçando que o SET ROWCOUNT está marcado como Deprecated, ou seja, ele pode ter o comportamento alterado em alguma versão futura do SQL Server.

Referência: https://docs.microsoft.com/pt-br/sql/database-engine/deprecated-database-engine-features-in-sql-server-2016?view=sql-server-ver15

“Entendi os conceitos Luiz, mas onde isso pode dar algum problema??? Parece ser algo tão simples e útil!”

Pois é, mas algumas coisas simples às vezes podem fazer um grande estrago. Vamos ver algumas situações.


SET ROWCOUNT – Exemplos:

Exemplo 1 – ROWCOUNT x UPDATE:

Nesse primeiro exemplo quero mostrar que você pode utilizar o ROWCOUNT para atualizar alguns registros. Aqui vou atualizar o ID de apenas 5 registros da tabela.

Exemplo 2 – ROWCOUNT x DELETE:

Agora vou mostrar que você também pode utilizar o ROWCOUNT para excluir alguns registros. Aqui vou excluir 5 registros da tabela.

Segue dois pontos que quero ressaltar aqui:

  • Ao alterar o valor do ROWCOUNT, todos os comandos da sessão atual serão afetados. Por exemplo, se você alterar o valor do ROWCOUNT para 100, todos os SELECTs da sessão atual irão retornar no máximo 100 registros. Portanto, tome cuidado e fique ciente dos impactos que isso pode causar OK.
  • O ROWCOUNT pode ter o comportamento alterado em alguma versão futura (está marcado como Deprecated). Já pensou se ao invés de atualizar ou excluir apenas 5 registros ele altere a tabela inteira ou não altere nada? Qual impacto isso pode causar na sua aplicação? Portanto, pense nisso se for optar por utilizar o ROWCOUNT.

Exemplo 3 – ROWCOUNT x PROCEDURES:

Vamos aumentar um pouco a dificuldade. Vou criar uma procedure bem simples que retorna todos os registros da tabela.

Será que o SELECT dentro dessa procedure também será afetado pelo SET ROWCOUNT? Bora testar!

Repare que o ROWCOUNT também afeta as procedures que você executa dentro da sessão que foi alterada. Isso pode ter um impacto muito grave, pois o resultado da procedure pode ser comprometido também!

Vamos ver mais um exemplo, dessa vez utilizando uma procedure que lista todos os registros da tabela, guarda em uma tabela temporária e por fim faz uma soma utilizando essa tabela temporária.

“Caramba Luiz, o resultado fica diferente mesmo que doidera!!!”

Pois é, quando utilizamos o ROWCOUNT 5, o SQL Server vai guardar apenas os 5 primeiros registros na tabela temporária e por fim vai fazer a soma (1 + 2 + 3 + 4 + 5 = 15). Viu como ele pode ser perigoso!!!


TOP – Conceitos e Exemplos:

“Entendi Luiz, mas como eu posso evitar esses problemas do ROWCOUNT?”

Eu particularmente sempre utilizo o TOP ao invés do ROWCOUNT, pois o TOP afeta apenas a operação que ele é utilizado e não gera um impacto ou efeito colateral nos demais trechos do código (inclusive nas procedures).

Referência: https://docs.microsoft.com/pt-br/sql/t-sql/queries/top-transact-sql?view=sql-server-ver15

Aqui vou atualizar o valor do ID de apenas 5 registros aleatórios da tabela (cuidado que nem sempre vai ser os 5 primeiros, a ordenação só é garantida quando você utilizar o ORDER BY #ficaadica).

Repare que o SELECT após o UPDATE não será afetado e irá retornar todos os registros da tabela, diferentemente do ROWCOUNT que teria limitado a quantidade de registros dele também.

Podemos também utilizar o TOP com o PERCENT para fazer algo com apenas um determinado percentual dos registros de uma tabela. No próximo exemplo, vou atualizar o valor do ID de apenas 20% dos registros da tabela (20% de 10 registros = 2 registros).

Novamente o SELECT após o UPDATE não foi afetado e retornou todos os registros da tabela. Ou seja, realmente o TOP só afeta a operação que ele é utilizado e não afeta as operações posteriores.


Resposta:

Agora espero que você tenha aprendido um pouco mais sobre o SET ROWCOUNT e o TOP e seja capaz de dar a resposta correta.

“Quantas linhas serão retornadas no script abaixo?”

Resposta Correta:

D) 5, 5


Espero que tenha gostado e que isso também possa ser útil no seu dia a dia. Até o próximo post!

Me siga no LinkedIn e YouTube para ficar por dentro das novidades.

Abraço,

Luiz Vitor França Lima

Consultor SQL Server

4 comentários em “Casos do Dia a Dia – Cuidado ao utilizar o comando SET ROWCOUNT

  1. José Dız (Porto SQL) Responder

    Ótimo alerta, Luiz. Principalmente quando estamos analisando o funcionamento de trecho de código SQL e não compreendemos porque os resultados retornados são inesperados.

    Esse recurso provavelmente está presente em códigos SQL antigos. Mais um item a observar na pós-migração de bancos de dados antigos para versões atuais do SQL Server.

    • Luiz Lima Autor do postResponder

      Obrigado José! Recentemente analisei uma procedure em um cliente que utiliza esse comando e ao testar uma alteração o resultado estava ficando diferente. Por isso resolvi fazer o post e alertar o pessoal =)

      Abraço,
      Luiz Vitor

  2. Jota Responder

    Eu uso o rowcount para controle de quantidade máxima de transação
    Por exemplo, suponha uma tabela tb1 com milhões de linhas e preciso limpar dados dela com algum critério:

    declare @qtd=1
    set rowcount= 10000

    while @qtd>0
    begin
    begin tran
    delete from tb1 where condicao
    select @qtd = @@rowcount
    commit tran

    end

    Nesse caso ele vai commitar a cada 10 mil linhas e evitar estourar o tempdb, melhorando o desempenho e não afetando tantos os outros usuários com problemas de lentidão.
    Quando não houver mais registros, @@rowcount será zero e sairá do looping

    • Luiz Lima Autor do postResponder

      Boa Jota,

      Interessante a forma que usou o rowcount. Quando faço DELETE com LOOP normalmente utilizo alguma variável para fazer esse controle.

      Obrigado por compartilhar o seu caso e realmente é uma boa prática fazer o DELETE aos poucos para a transação não ficar muito grande.

      Abraço,
      Luiz Lima

Deixe uma resposta