Buscar este blog

miércoles, 11 de septiembre de 2013

Compute Scalar - Qué es y Cómo Evitarlo

Amigos,

    Hoy les quiero hablar acerca de un operador llamado "Computer Scalar", presente en nuestros planes de ejecución frecuentemente.

   Como su nombre lo sugiere el operador indica la presencia de un cómputo escalar que devuelve un valor. También podemos hallar este  operador  dentro de nuestro plan de ejecución cuando realizamos conversiones explícitas o implícitas dentro de nuestas queries.
  
   Normalmente ignoramos al "Compute Scalar" ya que no representa un costo grande dentro del contexto de nuestro plan de ejecución  mas debemos tratar de tomar las medidas necesarias para evitarlo cuando realizamos operaciones de conversión o cálculos dentro de cursores grandes o de  loops de dimensiones considerables  Por qué? Porque pueden generar grandes cargas en nuestro server a nivel cpu, derivando esto en problemas de performance.   
   
   Les dejo pues un ejemplo de una operación que genera un "Computer Scalar" y abajo el cómo evitar el operador obteniendo grandes ganacias de performance con el mismo resultado final.

  1)  Operación que genera Computer Scalar:
 
  DECLARE @Count Int
 SET @Count = 0
 WHILE @Count < 100000000
 BEGIN
  IF EXISTS(SELECT Idcdr FROM Tabla WHERE Idcdr = @I)
     Begin
      --Accion
     End
  SET @Count = @Count + 1;
 END

  
  2)  Evitando el  Computer Scalar con el uso del @@ROWCOUNT en lugar del Exists

 DECLARE @Count Int
 SET @Count = 0
 WHILE @Count < 1000000
 BEGIN
   SELECT Idcdr FROM Tabla WHERE Id = @I
   IF @@ROWCOUNT > 0
   BEGIN
     -- Accion
   END
   SET @Count = @Count + 1;
 END

  Amigos "de yapa" y para que entiendan lo importante que es definir una variable corréctamente les dejo un ejemplo más que genera un Compute Scalar por "conversión implícita" y la solución como paso 2 del ejemplo:

 1)  Buscando el Máximo Idcdr (campo integer) dentro de una tabla produciendo un  Compute Scalar por mala definición de la variable @idini defnida como numeric (18,0) (cuando el campo idcdr de la tabla cdr_subscriptions es integer)

 -- Declaro variables --
Declare @idini numeric

-- Determino el Idcr maximo de la Tabla -
Select @idini =  coalesce (max(idcdr),0)
from cdr_subscriptions with (nolock)
 
** La mala definición de la variable genera una conversión implícita de numeric a integer lo que a su vez produce un "Compute Scalar"

  2)  Buscando el Máximo Idcdr (campo integer) dentro de una tabla eliminando el  Compute Scalar definiendo corréctamente la variable @idini  -esta vez como integer-

 -- Declaro variables --
Declare @idini integer

-- Determino el Idcr maximo de la Tabla -
Select @idini =  coalesce (max(idcdr),0)
from cdr_subscriptions with (nolock)
 

 Amigos, espero que lo expuesto haya sido de vuestra utilidad, les dejo mi saludo y como siempre estoy a vuestra disposición.

 Gustavo Herrera.


miércoles, 4 de septiembre de 2013

Espacio Ocupado Por Cada Una de Las Tablas En La Base de Datos

Amigos,

     Hoy quiero compartir un script muy útil que he preparado para uds.

     Cuántas veces notamos que nuestras bases de datos crecen a un rítmo importante sin saber exáctamente cuál o cuáles son las tablas "culpables" de ese crecimiento?

     Cuánta veces hemos pensado al ver nuestras tablas por consola "mmmm me parece que los índices ya representan mucho del espacio ocupado por la totalidad de la tabla..Debería analizar esto..."

      Pues bien, les traigo la solución...;  Un simple script que, haciendo uso de un sp del sistema llamado sp_spaceused,  nos permite listar para cada tabla de usuario de la base de datos en la que lo ejecutemos lo siguiente: (en orden descendente por la cantidad de registros de cada tabla):

    A - Epacio total ocupado por la tabla (MB)                                            
    B - Espacio en la tabla utilizado por datos (MB)                                       
    C - % del espacio utilizado por datos                                                  
    D - Espacio en la tabla utilizado por índices (MB)                                     
    E - % del espacio utilizado por indices                                                -- 
    D - Cantidad total de registros de las tabla

     Solo les queda probarlo... si tienen dudas... como siempre estoy a vuestra disposición...



BEGIN

-- Declaro Variable
Declare @object_name as varchar(50)

-- Declaro Tabla Temporal
CREATE TABLE #results
  (name varchar(50),
   filas integer,
   reserved varchar(50),
   data varchar(50),
   index_size varchar(50),
   Unused varchar(50))


/* Cursor_Tablas -Guarda en la tabla #result
 el resultado de la ejecución del sp_spaceused
sobre c/u de las tablas de usuario de la BD */

DECLARE Cursor_Tablas
Cursor For

Select distinct(s.name + '.' + o.name)
      from sys.schemas s
INNER JOIN sys.objects o  
ON o.schema_id = s.schema_id
Where
      type = 'U'  --Tablas de usuario

OPEN Cursor_Tablas
Fetch Next From  Cursor_Tablas
Into @object_name

WHILE @@FETCH_STATUS = 0
 BEGIN
   Insert Into #results
   EXEC sp_spaceused @object_name
   Fetch Next From cursor_tablas
   Into @object_name
 END;

Close Cursor_Tablas;
Deallocate Cursor_Tablas;

-- Se quita el "KB" de la tabla #result --
UPDATE
#results
SET
reserved = LEFT(reserved,LEN(reserved)-3),
data = LEFT(data,LEN(data)-3),
index_size = LEFT(index_size,LEN(index_size)-3),
unused = LEFT(unused,LEN(unused)-3)

-- Se listan las tablas ordenadas descendentemente por la cantidad de registros --
SELECT
distinct(t.Name) AS 'Table',
reserved/1024 as '[Disco (MB)]',
data/1024 as '[Datos (MB)]',
case when (data/1024) = 0 then 0.00 else (((data/1024)*100)/(reserved/1024)) end as '[% Datos (MB)]',
index_size/1024 as '[Idx (MB)]',
case when (data/1024) = 0 then 0.00 else 100-(((data/1024)*100)/(reserved/1024)) end as '[% Index (MB)]',
filas AS  'Records'
FROM #results as t
Inner Join sys.objects o
ON o.name =  t.name
Where
reserved is not null and
o.Type <> 'S' AND 
O.Type <> 'IT'     
order by
filas desc

--Eliminar la tabla temporal
Drop Table #results