jueves, 17 de enero de 2013

Programación en SQL Server - parte 2



En la entrada anterior hablábamos de los aspectos iniciales de la sentecia SELECT, en ésta platicaremos acerca de las implicaciones que tiene la palabra reservada JOIN.

La palabra reservada JOIN nos sirve para establecer un criterio a través del cual elegiremos filas o datos que queremos recuperar de dos tablas que comparten valores en una o más columnas (podría ser un valor calculado al vuelo pero no se recomienda dado que significaría en una sobrecarga de trabajo)

Llamaremos tabla "izquierda" a la que aparece primero en la sentencia y tabla "derecha" la que aparece después, por ejemplo:

SELECT I.Col1, I.Col2, D.Col1, D.Col2
FROM Izquierda I
INNER JOIN Derecha D
ON I.Col1 = D.Col1

INNER JOIN nos devolverá únicamente aquellas filas que cumplan con el criterio o criterios definidos en la parte del "ON", en este caso sólo devolvería las filas que tengan el mismo valor en Col1 de la tabla Izquierda y Col1 de la tabla Derecha.

Es común que en las bases de datos entidad - relación se haga JOIN entre dos tablas relacionadas a través de una llave foránea, lo cual no es obligatorio; si esta tarea se ejecuta forma habitual entonces deberemos valorar el crear un índice que cubra las columnas que conforman a la llave foránea.    Por ejemplo, supongamos que tenemos una tabla de Categorías y otra tabla de Productos:

CREATE TABLE Categories(
    Id SMALLINT IDENTITY(1, 1),
    Name VARCHAR(10) NOT NULL,
    CONSTRAINT pkCategories PRIMARY KEY (Id)
)
GO
CREATE TABLE Products(
    Id SMALLINT IDENTITY(1, 1),
    Id_Category SMALLINT NOT NULL,
    Name VARCHAR(10) NOT NULL DEFAULT SUBSTRING(REPLACE(CAST(NEWID() AS VARCHAR(36)), '-', ''), 1, 10),
    CONSTRAINT pkProducts PRIMARY KEY (Id),
    CONSTRAINT fkProducts_Categories FOREIGN KEY (Id_Category) REFERENCES Categories (Id)
)

Y que han sido llenadas con el siguiente código:

INSERT INTO Categories (Name) VALUES ('Category 1')
INSERT INTO Categories (Name) VALUES ('Category 2')
INSERT INTO Categories (Name) VALUES ('Category 3')
INSERT INTO Categories (Name) VALUES ('Category 4')
INSERT INTO Categories (Name) VALUES ('Category 5')
GO
DECLARE @i SMALLINT = 1
WHILE @i <= 5000
BEGIN
    INSERT INTO Products (Id_Category)
    VALUES ((ABS(CHECKSUM(NEWID())) % 5) + 1)

    SET @i = @i + 1
END

Si el sistema pide que los productos sean filtrados por categoría para ser mostrados al cliente, tendríamos un query como este:

SELECT P.Name, C.Name AS Category
FROM Products P
INNER JOIN Categories C
ON P.Id_Category = C.Id
WHERE C.Id = 3

Si observamos el plan de ejecución del query, veremos que se está llevando a cabo un barrido del CLUSTERED INDEX pkProducts, lo cual implica en un trabajo prolongado y que consume recursos.


El costo aproximado de este query es de: 0.260062

Tal como lo había mencionado antes, si es habitual la ejecución de este query, es súper recomendable crear un índice que cubra las columnas que son utilizadas en la parte del "ON".    En este caso también incluiremos la columna "Name" en el covering index para que el resultado se tenga a la mano en la estructura misma del índice:


CREATE INDEX ixProducts_Category
ON Products (Id_Category)
INCLUDE (Name)

Calculemos de nuevo el plan de ejecución y veremos que ha cambiado el barrido físico por una búsqueda en el índice que acabamos de crear, si analizamos el costo reportado por SQL Server veremos que es de: 0.0799945, lo cual significa una mejora del 325.09%, que se verá reflejada en una rápida lectura y también en un menor consumo de recursos.



En la siguiente entrada veremos el uso del LEFT JOIN, espero te haya sido de utilidad.

No hay comentarios.:

Publicar un comentario