Inicial > SQL Server > Operador do Dia – Compute Scalar

Operador do Dia – Compute Scalar

 

clip_image001  Compute Scalar

O operador de hoje, é o compute scalar, e ele esta presente na maioria dos planos de execução que vemos.

Como o próprio nome já diz o Compute Scalar efetua um determinado calculo e retorna o valor calculado, este calculo pode ser uma conversão, uma concatenação e outros…

Quase sempre ele é passado despercebido por apresentar um custo mínimo em relação a query, mas ele pode ser um ponto de atenção quando estamos falando de cursores, e loops muito grandes. Principalmente se estiver com problema de CPU.

Por exemplo, utilizar o Compute Scalar para fazer uma simples conversão pode ser tranqüilo, mas execute isso 1 milhão de vezes e verá que se você conseguir evitar este passo terá um ganho de CPU, e por conseqüência terá ganho de tempo.

USE tempdb

GO

CREATE TABLE TABTeste(ID   Int Identity(1,1) PRIMARY KEY,

                      Nome VarChar(250)      DEFAULT NewID())

GO

SET NOCOUNT ON

GO

INSERT INTO TABTeste DEFAULT VALUES

GO 10000

O comando acima cria uma tabela e inclui 10000 registros.

Agora vamos fazer um loop para o SQL passar pelo código 1 milhão de vezes.

DECLARE @I Int

SET @I = 0

WHILE @I < 1000000

BEGIN

  IF EXISTS(SELECT ID FROM TABTeste WHERE ID = @I)

  BEGIN

    PRINT ‘Entrou no IF’

  END

  SET @I = @I + 1;

END

GO

Plano de execução estimado:

clip_image002

 Repare que o SQL Server utilizou o Compute Scalar, vamos olhar melhor no plano em modo texto.

|–Compute Scalar(DEFINE:([Expr1003]=CASE WHEN [Expr1004] THEN (1) ELSE (0) END))

        |–Nested Loops(Left Semi Join, DEFINE:([Expr1004] = [PROBE VALUE]))

             |–Constant Scan

             |–Clustered Index Seek(OBJECT:([tempdb].[dbo].[TABTeste].[PK__TABTeste__3214EC27096F09E1]), SEEK:([tempdb].[dbo].[TABTeste].[ID]=[@I]) ORDERED FORWARD)

Conforme podemos observar o Compute Scalar foi Utilizado para verificar se o Nested Loop retornou alguma coisa ou não, ou seja, ele está fazendo o papel do IF EXISTS…

O resultado de execução da query e o uso de CPU está na imagem abaixo, gerada pelo Profiler.

clip_image003

Agora vamos alterar o código para remover o uso do Compute Scalar

DECLARE @I Int, @Var Int

SET @I = 0

WHILE @I < 1000000

BEGIN

  SELECT @Var = ID FROM TABTeste WHERE ID = @I

  IF @@ROWCOUNT > 0

  BEGIN

    PRINT ‘Entrou no IF’

  END

  SET @I = @I + 1;

END

GO

Plano de execução estimado:

clip_image004

       |–Clustered Index Seek(OBJECT:([tempdb].[dbo].[TABTeste].[PK__TABTeste__3214EC27096F09E1]), SEEK:([tempdb].[dbo].[TABTeste].[ID]=[@I]) ORDERED FORWARD)

Desta vez o SQL não utilizou o Compute Scalar, vamos ver o resultado no profiler.

clip_image005

Como você pode observar o SQL utilizou menos CPU e terminou a execução em nos tempo. Não estou querendo mostrar qual é a melhor forma de fazer uma validação, apenas que exibir o uso do operador. De qualquer forma, fica ai a dica do uso da @@RowCount. Eu já mudei um código onde tinha uns 30 IF EXISTS dentro de um loop desses e o resultado foi bem satisfatório.

Vamos ver mais do Compute Scalar na pratica.

DECLARE @Tab TABLE(ID SmallInt PRIMARY KEY)

SELECT ‘Fabiano’ + ‘ – ‘ + ‘Amorim’ FROM @Tab

clip_image006

SELECT ‘Fabiano’ + ‘ – ‘ + ‘Amorim’ FROM @Tab

  |–Compute Scalar(DEFINE:([Expr1003]=’Fabiano – Amorim’))

       |–Clustered Index Scan(OBJECT:(@Tab))

O plano gerado foi exatamente para fazer a concatenação, simples não?

Agora vamos ver um comportamento bem interessante do Compute Scalar, que mudou a partir do SQL Server 2005.

Imagine o seguinte código:

DECLARE @Tab TABLE(ID SmallInt PRIMARY KEY)

DECLARE @ID_Int Integer

SELECT *

  FROM @Tab

 WHERE ID = @ID_Int

Repare que a coluna ID é do tipo SmallInt e a variável @ID_Int é do tipo Integer, ou seja o SQL terá que converter o valor de @ID_Int para poder efetuar a comparação com a coluna ID.

No SQL Server 2000 ele gera o seguinte plano:

clip_image007

SELECT *    FROM @Tab   WHERE ID = @ID_Int

  |–Nested Loops(Inner Join, OUTER REFERENCES:([Expr1002], [Expr1003], [Expr1004]))

       |–Compute Scalar(DEFINE:([Expr1002]=Convert([@ID_Int])-1, [Expr1003]=Convert([@ID_Int])+1, [Expr1004]=If (Convert([@ID_Int])-1=NULL) then 0 else 6|If (Convert([@ID_Int])+1=NULL) then 0 else 10))

       |    |–Constant Scan

       |–Clustered Index Seek(OBJECT:(@Tab), SEEK:(@Tab.[ID] > [Expr1002] AND @Tab.[ID] < [Expr1003]),  WHERE:(Convert(@Tab.[ID])=[@ID_Int]) ORDERED FORWARD)

Uau, que trampo eim? Agora vamos ver o que acontece se eu rodar este código no SQL 2005 ou 2008.

Plano de execução no SQL Server 2005

clip_image008

SELECT *    FROM @Tab   WHERE ID = @ID_Int

  |–Clustered Index Seek(OBJECT:(@Tab), SEEK:([ID]=[@ID_Int]) ORDERED FORWARD)

Aaaa, agora sim… Ficou fácil não? Ué mas o SQL não precisa mais converter o valor?

Vamos dar uma olhada melhor nos hints do operador Clustered Index Seek.

clip_image009

O time de desenvolvimento do Query Optimizer mudou um pouco a coisa, agora o SQL utiliza a função “Scalar Operator” para converter o valor de @ID_Int para o tipo da coluna ID.

Interessante…

Por hoje é só, até segunda-feira com o próximo Operador do dia.

Categorias:SQL Server
  1. Nenhum comentário ainda.
  1. No trackbacks yet.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

%d blogueiros gostam disto: