Curso SQL: Triggers

En una nota pasada hablamos sobre los procedimientos almacenados que se pueden usar en SQL, pueden revisar la información aquí antes de leer esta nota para tener una mejor idea de lo que se explicará.

En esta ocasión explicaremos el uso de los triggers, también conocidos como disparadores o desencadenadores, estos son un tipo de procedimiento almacenado especial que se ejecuta de manera automática cuando sucede un evento específico en el servidor de base de datos.

Existen 3 distintas categorías de triggers:

  • Trigger DML: se ejecutan cuando un usuario  intenta modificar datos mediante un evento de lenguaje de manipulación de datos (DML). Estos eventos son instrucciones INSERT, UPDATE o DELETE de una tabla o vista.
  • Trigger DDL:  se ejecutan en respuesta a una variedad de eventos de lenguaje de definición de datos (DDL). Estos corresponden principalmente a instrucciones CREATE, ALTER y DROP de Transact-SQL, y a determinados procedimientos almacenados del sistema que ejecutan operaciones de tipo DDL.
  • Trigger Logon:  se activan en respuesta al evento LOGON que se genera cuando se establece la sesión de un usuario.

En pocas palabras, un trigger es diseñado para comprobar o cambiar los datos con base en una instrucción de modificación o definición de datos; no debe devolver datos al usuario por lo que se aconseja no incluir en un trigger instrucciones SELECT que devuelven resultados ni las instrucciones que realizan una asignación variable.

Triggers en SQL

Creación de triggers

La estructura de la instrucción para la creación de un trigger es como sigue:

 CREATE TRIGGER  NombreTrigger ON {tabla | vista } [,...n] ]
      {FOR|AFTER|INSTEAD OF} {[INSERT][,][UPDATE][,][DELETE]} 
      AS sentencia_sql  [;] [,...n ] 

 

Colocamos la sentencia para crear el trigger CREATE TRIGGER seguido del nombre que queramos y señalamos la tabla o vista en la que será aplicable. Esta sentencia debe ser la primera en el lote a ejecutar.

Siguiendo eso se debe colocar el tipo de instrucción de modificación que activará el trigger ya sea INSERT, DELETE, UPDATE o combinaciones de estas.


AFTER
indica que el trigger sólo se activa cuando todas las operaciones especificadas en la instrucción SQL desencadenadora se han ejecutado correctamente.

INSTEAD OF indica que se ejecuta el trigger en vez de la instrucción SQL que provocó su activación, por lo que se suplantan las acciones de las instrucciones desencadenadoras.

Las siguientes instrucciones Transact-SQL no están permitidas en un desencadenador DML:

ALTER DATABASE, CREATE DATABASE, DROP DATABASE.

 

Con esto, veamos un ejemplo concreto de un trigger:


CREATE TRIGGER trVentaD ON VentaD FOR INSERT

AS

BEGIN

DECLARE @Cantidad INT

SELECT @Cantidad=Cantidad FROM INSERTED

    IF @Cantidad < 2

    BEGIN

          PRINT 'La cantidad mínima de venta son 2 artículos'

          ROLLBACK TRANSACTION

 END

END

 

Dicho trigger se ejecuta en caso de que una venta se lleve a cabo que cuente con menos de 2 artículos en cantidad. Por ejemplo, la siguiente instrucción activaría nuestro trigger:


INSERT INTO VentaD (ID,Articulo,Cantidad,Precio,Impuestos)

VALUES(46827, 'MG-11/2CF', 1, 450.00, 45.00)

 

Ahora bien, se debe saber que si se desea modificar el contenido de un trigger, se puede hacer mediante la sentencia ALTER TRIGGER, sin embargo, si se quiere cambiar el nombre deberá eliminarse primero y volverse a crear, para eliminarlo es necesario usar DROP TRIGGER.


Deshabilitar y habilitar un Trigger

En ocasiones puede ser útil inhabilitar temporalmente un desencadenador sin que por ello suponga eliminarlo, para estos casos podemos utilizar la sentencia DISABLE TRIGGER.

Por ejemplo, para deshabilitar el trigger del ejemplo anterior sería como sigue:


DISABLE TRIGGER trVentaD ON VentaD

 

Además, si queremos deshabilitar todos los triggers en una base de datos es posible con la siguiente sentencia:


DISABLE TRIGGER ALL ON DATABASE

 

Ahora bien, para poder revertir lo que hemos hecho, basta con utilizar ENABLE TRIGGER de la siguiente manera:


ENABLE TRIGGER trVentaD ON VentaD

 

De la misma forma podemos habilitar todos los trigger en una base de datos así:


ENABLE TRIGGER ALL ON DATABASE

 


Eliminar un trigger

Como se había mencionado con anterioridad, si se necesita eliminar un trigger de manera permanente, se debe usar una sentencia DROP TRIGGER.

Siguiendo con nuestro ejemplo inicial, la manera de eliminarlo sería como sigue:


DROP TRIGGER trVentaD

 

Estas son las bases que se deben saber al momento de utilizar triggers en SQL, estos pueden llegar a ser muy útiles, sobretodo si su base de datos tiene mucha actividad sensible respecto a insertar, eliminar y actualizar registros ya que se pueden utilizar como validaciones como el ejemplo que manejamos en esta nota.

Desde algo tan simple como eso, hasta un trigger verificador que nos permita identificar si, por ejemplo, un registro de un correo electrónico es válido basándonos en su formato (que tenga el símbolo “@”, que tenga al menos un carácter “.”, etc.).

Por lo que el límite es solo el ingenio que el programador pueda tener, ya que estos procedimientos almacenados tan especiales pueden ser usados de muchas maneras.

Espero les sea de utilidad, experimenten que tan lejos pueden llegar con los triggers y si tienen alguna duda o cualquier comentario sobre el tema, podemos hablar de ello en la sección de los comentarios.

¡Nos leemos hasta la otra!


Referencias

Curso SQL: procedimientos almacenados

Un procedimiento almacenado (STORE PROCEDURE) está conformado por un conjunto de instrucciones Transact-SQL que definen un determinado proceso, este puede aceptar parámetros de entrada y devolver un valor o conjunto de resultados. Este procedimiento se guarda en el servidor y puede ser ejecutado en cualquier momento.

La diferencia entre los procedimientos almacenados y las instrucciones SQL o lotes de estas reside en que los procedimientos están precompilados. De esta manera la mayor parte del trabajo de procesamiento de consultas ya está hecho y almacenado en una tabla del sistema por lo que el uso de procedimientos almacenados mejora notablemente la potencia y eficacia del SQL.

Hay algunos procedimientos incorporados del sistema en la base de datos master, todos tienen antes de su nombre las letras “sp_” y por lo regular se intenta que los procedimientos creados por usuarios sigan una regla similar para nombrarlos pero no exactamente igual para no confundirlos.

Los procedimientos almacenados se crean mediante la sentencia CREATE PROCEDURE y se ejecutan con EXEC (o EXECUTE). Para ejecutarlo también se puede utilizar el nombre del procedimiento almacenado sólo, siempre que sea la primera palabra del lote.


Crear y ejecutar un procedimiento

Para crear un procedimiento almacenado como hemos dicho se emplea la instrucción CREATE PROCEDURE:

CREATE PROCEDURE:

CREATE {PROC|PROCEDURE} 
[NombreEsquema.]NombreProcedimiento          
[{@parametro tipo} [VARYING] [= valorPredet]  [OUT|OUTPUT] ] [,...n]     AS
{  
   <bloque_instrucciones> [ ...n]
}[;]     

<bloque_instrucciones> ::=   
{
   [BEGIN] instrucciones [END]
}

Las instrucciones CREATE PROCEDURE no se pueden combinar con otras instrucciones SQL en el mismo lote.

Luego del verbo se debe poner el nombre del procedimiento que no debe superar los 128 caracteres.

Además, se pueden declarar uno o más parámetros indicando para cada uno su nombre (debe de empezar  por arroba) y su tipo de datos, y opcionalmente un valor por defecto (=valorPredet) este valor será el asumido si en la llamada el usuario no pasa ningún valor para el parámetro. Un procedimiento almacenado puede tener un máximo de 2.100 parámetros.

Ahora bien, un ejemplo rápido de lo que hemos visto:

CREATE PROCEDURE  spHola_Mundo 
AS
PRINT ‘Hola Mundo’;     
GO

-Se indica “GO” para cerrar el lote que crea el procedimiento y empezar  otro lote.

EXEC spHola_Mundo;

-De esta forma llamamos al procedimiento y se ejecuta.

Este procedimiento lo que hace simplemente es imprimir en consola “Hola Mundo” cuando es llamado.

Ahora probemos con un parámetro de entrada (la frase que queremos que escriba).

CREATE PROCEDURE spDice_Frase  @frase CHAR(30)    
AS      
PRINT @frase;  
GO     
EXEC spDice_Frase ‘Mulieres es genial’;


Parámetros de salida

Un procedimiento puede hacer uso no solo de parámetros de entrada sino que también puede devolver datos y para ello se debe indicar al parámetro con la palabra OUTPUT o bien usando la instrucción RETURN.

Para ello hagamos un ejemplo:

CREATE PROC spUltimo_cumple @ofi  INT, @fecha DATETIME OUTPUT    
AS      
SELECT @fecha=(SELECT MAX(fechaNacimiento)  FROM empleados WHERE oficina=@ofi)

En el parámetro de fecha OUTPUT recopilamos entonces el último cumpleaños de los empleados registrados en la oficina que indiquemos en el parámetro de entrada.

En la llamada, para los parámetros de salida, en lugar de indicar un valor de entrada se indica un nombre de variable, misma que recogerá el valor devuelto por el procedimiento sin olvidar la palabra OUTPUT:

DECLARE @cumple AS DATETIME;   
EXEC spUltimo_cumple 12,@cumple  OUTPUT;   
PRINT @cumple;

Además existe la instrucción RETURN que ordena salir de un procedimiento en cualquier punto en que esta instrucción se encuentre. Así bien, se utiliza para devolver valores al final o quizás en alguna consulta intermedia de algún procedimiento.

Pero algo importante a resaltar es que RETURN no puede regresar un valor NULL ya que de intentarlo saltará un mensaje de error y advertencia.

Supongamos que queremos saber el número de clientes en cierto municipio, con este procedimiento con RETURN se puede hacer de la siguiente manera:

CREATE PROC spClientesTotales @municipio INT   
AS    
RETURN (SELECT COUNT(*) FROM  clientes WHERE municipio=@municipio)   
GO

O bien asignar el resultado de la consulta a una variable y devolver la variable.


Insertar datos con INSERT EXEC

Con esta instrucción se pueden insertar los datos resultantes de un procedimiento en una tabla destino que se especifique.

Primeramente creamos nuestro procedimiento que en este caso recopila todas las ventas (pedidos) hechas a un país que es un parámetro de entrada:

CREATE PROCEDURE spVentasPorPais @paisOrden AS VARCHAR(15)
AS
SELECT id, empid, fechaOrden, pais, art FROM Ordenes WHERE pais = @paisOrden;

Necesitamos entonces ejecutar el siguiente código para que el conjunto de resultados de la consulta sea insertado en la tabla MisOrdenes.

SET IDENTITY_INSERT MisOrdenes ON;


INSERT INTO MisOrdenes(id,empid,fechaOrden,pais,art)
EXEC spVentasPorPais @pais = ‘España’;


SET IDENTITY_INSERT MisOrdenes OFF;

Esto puede resultar útil en ciertas ocasiones, no siempre es lo más utilizado pero es mejor saber todas nuestras opciones y que cada uno decida el mejor camino a seguir.


Eliminar y alterar un procedimiento

Por último, a veces puede suceder que necesitemos alterar o incluso eliminar un procedimiento que con anterioridad hemos creado y eso es de lo más normal.

Para eliminar un procedimiento se debe usar la siguiente sentencia:

DROP PROCEDURE spVentasPorPais;

Mientras que para alterarlo podemos usar lo siguiente:

ALTER PROCEDURE spVentasPorPais

Y posteriormente escribir el procedimiento como si lo estuviésemos creando, es decir que al ejecutar dicha instrucción se “borrará” el anterior procedimiento y se reemplazará con el nuevo que hemos escrito.

No cabe duda que en el mundo de SQL los procedimientos almacenados son muy populares y utilizados, por lo que entender las bases es esencial.

Por la lección de hoy ha sido todo, pero si tienen alguna duda respecto al tema o sugerencias de conceptos y ejemplos de SQL que podemos abordar son bienvenidas en la sección de comentarios.

¡Nos leemos hasta la otra!

Referencias

https://www.w3schools.com/SQl

Documentación sobre SQL de Microsoft: https://docs.microsoft.com/en-us/sql

Imagen modificada: https://www.entechnie.com/wp-content/uploads/2017/05/SQL_Server.jpg

SQL básico: consultas resumen

Bienvenidos de nueva cuenta al curso de SQL básico, ahora que somos todos unos expertos en el uso de consultas multitabla, vamos a explorar algunas funciones y cláusulas nuevas que nos permitirán usar la sentencia SELECT para obtener resúmenes de los datos contenidos en nuestras tablas.

Cabe aclarar que una diferencia importante entre las consultas resumen y las otras que hemos explorado hasta el momento es que en las consultas normales las filas del resultado se obtienen directamente de las filas del origen de datos y cada dato que aparece en el resultado tiene su dato correspondiente en el origen de la consulta mientras que las filas generadas por las consultas resumen no representan datos del origen sino un total calculado sobre estos datos.

Funciones de agregado

Una función de agregado  acepta un grupo de datos (por lo regular una columna de datos) como argumento, y produce un único dato que resume el grupo. Por ejemplo la función AVG() acepta una columna de datos numéricos y devuelve la media aritmética (average) de los valores contenidos en la columna.

Todas las funciones de agregado tienen una estructura parecida:

Función ([ALL|DISTINCT] expression)

 

El grupo de valores sobre el que actúa la función lo determina el resultado de la expresión que será simplemente un nombre de columna o una expresión basada en una columna o varias del origen de datos.

La palabra ALL indica que se tiene que tomar en cuenta todos los valores de la columna. Si no se especifica, este es el valor por defecto.

Mientras que la palabra DISTINCT hace que se consideren todas las repeticiones del mismo valor como uno sólo (considera valores distintos).

A excepción de la función COUNT, todas las funciones de agregado ignoran los valores NULL.

Función COUNT

Su estructura es como sigue:

COUNT ({[ALL|DISTINCT] expression | * } )

 

La expresión puede ser de cualquier tipo excepto text, image o ntext.

Como se mencionó anteriormente, esta función cuenta por defecto los valores NULL y se utiliza ALL y DISTINCT según las necesidades del programador.

Un ejemplo rápido sería el siguiente:

SELECT COUNT(Nombre) AS 'No. Proveedores' from Prov

SELECT COUNT(Nombre) AS 'No. Proveedores' from Prov

Uno pensaría que el resultado es realmente el número de proveedores que se tienen registrados en la respectiva tabla, sin embargo siempre existe un poco de error humano o algún otro contratiempo que haga que este valor no sea del todo cierto, por lo que para estar seguros que los registros contabilizados no están repetidos podemos hacer lo siguiente:

SELECT COUNT(DISTINCT Nombre) AS 'No. Proveedores' from Prov

SELECT COUNT(DISTINCT Nombre) AS 'No. Proveedores' from Prov

Como podemos notar, de alguna manera había ciertos valores repetidos y ahora realmente tenemos el número de proveedores (por nombre) que hay registrados en nuestra tabla.

Otra forma interesante de utilizar esta función es utilizando (*) en lugar de una expresión, de esta forma la función devuelve el número de filas del origen después de ejecutar la cláusula WHERE.

SELECT COUNT(*) AS 'Proveedores de Nuevo León' 

FROM Prov 

WHERE Estado='Nuevo Leon'

SELECT COUNT(*) AS 'Proveedores de Nuevo León' FROM Prov WHERE Estado='Nuevo Leon'

En este ejemplo contamos a todos los proveedores que son específicamente del estado de Nuevo León, de esta forma tenemos más opciones para diseñar estas consultas y que sean de mayor utilidad según se necesite.

 

Funciones MIN y MAX

Decidí agrupar estas 2 funciones en una sola sección ya que hacen cosas opuestas y tienen la misma estructura.

MAX/MIN ([ALL|DISTINCT] expression)

 

Mientras que la función MAX devuelve el valor máximo de la expresión, la función MIN devuelve el valor mínimo, en ambos casos sin considerar los nulos.

Ambas funciones se pueden usar con columnas numéricas, de caracteres y de datetime, pero no con columnas de bit.

A continuación, un pequeño ejemplo para cada función.


SELECT MAX(PrecioUnitario) AS 'Precio Máximo' FROM Art

SELECT MAX(PrecioUnitario) AS 'Precio Máximo' FROM Art

En este ejemplo tomamos de nuestra tabla de artículo la columna de precio unitario y consultamos el precio máximo registrado.

SELECT MIN(PrecioUnitario) AS 'Precio Mínimo' FROM Art

SELECT MIN(PrecioUnitario) AS 'Precio Mínimo' FROM Art

Ahora se trata del caso opuesto, buscando en nuestra tabla el registro del precio mínimo de los artículos.


Función SUM

SUM ([ALL|DISTINCT] expression ) 

 

Su nombre casi lo explica ya que devuelve la suma de los valores conseguidos con la expresión utilizada. En este caso solo es utilizable con columnas numéricas.

SELECT SUM(Importe) AS 'Total de Ventas' FROM Venta

SELECT SUM(Importe) AS 'Total de Ventas' FROM Venta

Se hace una sumatoria de todos los importes de todas las ventas y así se puede conseguir un total de las ventas registradas (sí, en esta base de datos de prueba hay muchos registros).

 

Función AVG

AVG ([ALL|DISTINCT] expression)

 

Esta función devuelve el promedio de los valores de un grupo, dicho esto, cabe mencionar que se omiten los valores nulos.

Las situaciones ya mencionadas con anterioridad respecto a las expresiones aplican de la misma forma para esta función.

SELECT AVG(cd.Cantidad*cd.Precio) AS 'CostoPromedio' FROM CompraD AS cd 

SELECT AVG(cd.Cantidad*cd.Precio) AS 'CostoPromedio' FROM CompraD AS cd 

Esta consulta nos permite saber el costo promedio por orden de compra y a diferencia de los ejemplos anteriores, esta vez utilizamos una expresión distinta a solo el nombre de la columna.

 

Cláusula GROUP BY (Agrupamiento de filas)

Si han prestado atención podrán haber notado que los ejemplos de las funciones se componen totales de todas las filas origen con una única fila como resultado, sin embargo, a menudo se requiere obtener totales parciales ya sea para saber qué tanto ha vendido un empleado en específico o cuál artículo es el más solicitado.

En caso de que se quiera obtener este tipo de resultados es necesario utilizar la cláusula GROUP BY.

Una consulta con una cláusula GROUP BY agrupa los datos de la tabla origen y produce una única fila resultado por cada grupo formado. Las columnas indicadas en el GROUP BY se llaman columnas de agrupación o agrupamiento.

Cuando queremos realizar una agrupación múltiple, por varias columnas, éstas se indican en la cláusula GROUP BY en el orden de mayor a menor agrupación igual que con la cláusula ORDER BY.


SELECT cd.ID,AVG(cd.Cantidad*cd.Precio) AS 'CostoPromedio'

FROM CompraD AS cd

GROUP BY cd.ID

Esta consulta nos permite saber el costo promedio por orden de compra y a diferencia de los ejemplos anteriores, esta vez utilizamos una expresión distinta a solo el nombre de la columna. Cláusula GROUP BY (Agrupamiento de filas) Si han prestado atención podrán haber notado que los ejemplos de las funciones se componen totales de todas las filas origen con una única fila como resultado, sin embargo, a menudo se requiere obtener totales parciales ya sea para saber qué tanto ha vendido un empleado en específico o cuál artículo es el más solicitado. En caso de que se quiera obtener este tipo de resultados es necesario utilizar la cláusula GROUP BY. Una consulta con una cláusula GROUP BY agrupa los datos de la tabla origen y produce una única fila resultado por cada grupo formado. Las columnas indicadas en el GROUP BY se llaman columnas de agrupación o agrupamiento. Cuando queremos realizar una agrupación múltiple, por varias columnas, éstas se indican en la cláusula GROUP BY en el orden de mayor a menor agrupación igSELECT cd.ID,AVG(cd.Cantidad*cd.Precio) AS 'CostoPromedio' FROM CompraD AS cd GROUP BY cd.ID

Utilizando parte de una consulta de ejemplo anterior, tenemos el costo promedio pero esta vez por cada orden de compra, de tal manera la información puede llegar a ser más útil.


Cláusula HAVING(Selección sobre grupos de filas)

Cuando se quiere utilizar una cláusula de selección sobre filas de origen utilizamos WHERE pero en el caso de las consultas resumen en donde manejamos grupos de filas tenemos la cláusula HAVING.

Funciona de la misma manera que WHERE pero en lugar de actuar sobre las filas del origen de datos, HAVING actúa sobre las filas del resultado.

Siempre que en una condición de selección haya una función de columna, la condición deberá incluirse en la cláusula HAVING, además, HAVING filtra filas del resultado así que sólo puede contener expresiones (nombres de columnas, expresiones, funciones…) que también pueden aparecer en la lista de selección, por lo que se aplica la regla a no olvidar:

En la cláusula HAVING un nombre de columna no puede aparecer fuera de una función de agregado si no es una columna de agrupación.


SELECT COUNT(vd.Articulo) AS ‘Artículos Registrados’,vd.ID

FROM ventaD AS vd

GROUP BY vd.ID

HAVING COUNT(vd.Articulo)  < 3

SELECT COUNT(vd.Articulo) AS 'Artículos Registrados',vd.ID FROM ventaD AS vd GROUP BY vd.ID HAVING COUNT(vd.Articulo) < 3

Con esta consulta podemos obtener no la cantidad de artículos por orden de venta sino que especificamos que solo queremos las órdenes de venta con su respectivo registro de artículos que sean menores a 3.

Sabiendo ahora la manera particular en que cada una de estas funciones y cláusulas actúa, podemos explorar una gran variedad de posibilidades al combinarlas y diseñar consultas que obtengan los resultados que deseamos.

Esto es todo por esta lección, en la siguiente nota explicaremos el tema de las subconsultas, algo interesante y que se llega a necesitar con bastante frecuencia ya que se trata de una consulta dentro de otra consulta pero eso ya lo veremos a profundidad el siguiente mes.

Si tienen alguna duda respecto al tema tratado o incluso de SQL básico en general, no duden en preguntar en los comentarios.

¡Nos leemos hasta la otra!


Referencias

https://www.w3schools.com/SQl

Documentación sobre SQL de Microsoft: https://docs.microsoft.com/en-us/sql

Imágenes de la nota: screenshots tomadas por la autora en SQL Server.

Imagen principal modificada pero inicialmente tomada de: https://www.campusmvp.es/recursos/image.axd?picture=SQL-Lenguaje_1.png

SQL básico: consultas multitablas

Bienvenidos de nueva cuenta al curso básico de SQL, si por alguna razón llegaste a esta nota al azar, te invito a seguir el curso desde el inicio de la serie aquí: SQL básico: conceptos básicos.

En esta ocasión continuaremos hablando de consultas que podemos hacer pero esta vez centrándonos en la manera en que se crean pensando en la conexión de datos entre distintas tablas.

Cabe aclarar que cuando menciono “tablas” me refiero tanto a las que se encuentran  físicamente almacenadas en la base de datos como también a las temporales e incluso a las que son resultantes de alguna vista o consulta.

 

Unión de tablas UNION

Consiste en tomar 2 tablas y por consiguiente obtener otra tabla que contenga las filas de las 2 tablas de manera que aparezcan las filas de la primera tabla seguidas de las filas de la segunda tabla.

Se debe de tener en cuenta que para hacer una unión así, ambas tablas deben tener el mismo esquema (mismo número de columnas y tipos de datos compatibles) y que luego de la unión la tabla resultante hereda los encabezados de la primer tabla.

Ahora bien, vamos directamente a un ejemplo para continuar explorando el uso de UNION en consultas:

SELECT Articulo AS 'Artículos'
FROM Art
UNION
SELECT Articulo
FROM ArtAlm

 

En el ejemplo tenemos la unión de la tabla Art con la tabla ArtAlm, tratándose de las tablas que manejan  los artículos y estos mismos en almacenes. En ambos casos se pide en la consulta los datos de los artículos y en el caso de la tabla Art, se utiliza un alias con la palabra “AS” para nombrar a la columna de otra forma de tal manera que al hacer la consulta la tabla resultante tenga ese nombre de columna.

 

Union en SQL

 

En caso de encontrarse con elementos repetidos, el sistema por defecto eliminará dichas filas. Si se necesitara obtener todas las filas, incluyendo las repeticiones, se puede utilizar “UNION ALL”, esto ocasiona que la consulta se ejecute un poco más rápido ya que el sistema no tiene que estar eliminando las repeticiones.

También se debe de tener en cuenta que se pueden combinar más de 2 tablas si es que se cumplen con todos los requerimientos  e igualmente se puede hacer uso de las cláusulas que expliqué en la segunda nota de la serie: SQL básico: consultas simples.

 

Diferencia EXCEPT

Continuando con la combinación de dos o más tablas, llegamos a la que nos permitirá hacer una operación similar a una diferencia matemática.

Por lo que la tabla resultante contiene las filas de la primera consulta que no aparecen en la segunda.

Como se trata de una operación muy sencilla de comprender, vamos a un ejemplo rápido:

SELECT Articulo
FROM Art
EXCEPT
SELECT Articulo
FROM ventaD

De nuevo vamos con la tabla Art que maneja los datos de los artículos con la tabla ventaD que maneja los datos de los detalles de las ventas que se realizan.

La estructura y condiciones como vemos son las mismas que con UNION, dos consultas separadas en este caso por la palabra EXCEPT que marca hacia que lado se efectuará la operación.

Except en SQL

Como podemos observar, la tabla resultante es simplemente de aquellos artículos que se encuentran en la tabla Art (primera consulta), pero que no se encuentran en la tabla ventaD (segunda consulta).

Este tipo de consulta puede ser útil si lo que queremos es por ejemplo averiguar los artículos que tenemos en existencia que no han sido registrados como ventas.

 

Intersección INTERSECT

Esta tiene un funcionamiento parecido a las anteriores pero en la tabla resultante tenemos las filas que están simultáneamente en las dos o más consultas que utilicemos.

SELECT Articulo
FROM Art
INTERSECT
SELECT Articulo
FROM ventaD 

Siguiendo por la misma línea que hemos estado utilizando en estos ejemplos, ahora tenemos de nueva cuenta a la tabla Art y a la tabla ventaD. En esta ocasión con la operación de intersección buscamos los artículos que se encuentran registrados a su vez en ambas tablas.

Intersect en SQL

Este tipo de consultas pueden servir como en el ejemplo, para obtener una lista de artículo que se encuentren tanto en nuestra lista de artículos usual como en las de los detalles de las ventas, es decir, los artículos de los que tenemos registradas al menos una venta.

Por lo que es bastante útil si tienes tablas con mucha información en común pero quieres depurar de alguna manera a la hora de hacer consultas.

 

Composición de tablas

Las consultas y operaciones que hemos realizado hasta ahora han sido con tablas que tuviesen el mismo esquema, sin embargo, hay ocasiones en las que se necesita obtener una tabla resultado que tenga en la misma fila datos de distintas tablas.

Por ejemplo se necesita obtener pedidos y en esa misma fila obtener algunos datos del cliente como nombre y dirección, este tipo de consultas es posible y de hecho son muy usadas.

Nos apoyaremos en la composición de tablas para poder lograr consultas de este tipo, esto consiste en obtener a partir de dos tablas cualesquiera una nueva tabla fusionando las filas de una con las filas de la otra, concatenando los esquemas de ambas tablas. Básicamente se trata de armar parejas de filas.

Ahora continuaremos con la operación a partir de la cual están definidas las demás operaciones de composición de tabla, se trata del producto cartesiano.


Producto cartesiano CROSS JOIN

Este obtiene todas las posibles concatenaciones de filas de la primera tabla con filas de la segunda tabla. Para poder utilizar el CROSS JOIN debemos indicarlo en la cláusula FROM entre los nombres de las tablas que se quieran concatenar.

SELECT a.*, vd.*
FROM Agente AS a
CROSS JOIN
ventaD as vd 

En esta ocasión utilizamos las tablas de Agente y la de ventaD, al utilizar el asterisco estamos señalando que queremos que nos traigo todas las columnas de dicha tabla y utilizamos de nueva cuenta el “AS” para indicar un alias esta vez a las tablas para poder cualificar los campos, algo que se utiliza mucho sobretodo si se están utilizando consultas multitablas.

 

Cross Join en SQL

 

Cross Join en SQL

Mis tablas tienen muchas columnas, por lo que partí el resultado en 2 imágenes, pero se puede notar que primeramente se muestran las columnas de la tabla Agente y consiguientemente de la tabla ventaD.

El CROSS JOIN no es la operación más utilizada ya que por lo regular al componer 2 tablas lo que se busca es que se haga bajo una condición de selección basada en campos de combinación y para ello es más eficiente utilizar las siguientes operaciones.

Composición interna INNER JOIN

En esta ocasión esta composición es en la que los valores de las columnas combinadas se comparan mediante un operador de comparación específico. En palabras simples, es un tipo de producto cartesiano con una condición.

Es de las operaciones más utilizadas ya que es muy común querer combinar los registros de una tabla relacionada con registros correspondientes en una tabla de referencia como pueden ser los nombres de los agentes en una respectiva venta o los datos de una factura con los datos completos de tu proveedor.

SELECT v.Mov, v.MovID, v.cliente, c.nombre, c.Direccion
FROM Venta AS v
INNER JOIN
Cte AS c
ON v.Cliente=c.Cliente

INNER JOIN en SQL

En esta consulta combinamos la tabla venta con la tabla de clientes, obtenemos los movimientos que tengamos en ventas, sus identificadores y los de los clientes y por otro lado tenemos el nombre del cliente y su dirección.

Para la condición que un INNER JOIN nos exige tenemos una igualdad (que es por lo regular lo que se utiliza) como la última línea de nuestra consulta en la que igualamos el campo de cliente en la tabla de venta con el campo de cliente de la tabla cte, lo que significa que está haciendo la relación entre ambas tablas en donde nos traiga las parejas de filas según la información de ambas tablas bajo la condición de que se esté hablando del mismo cliente.

El punto débil del INNER JOIN es que al hacer una consulta con este, no aparecerán las filas que no tienen fila correspondiente en la otra tabla.

En el caso de nuestro ejemplo no aparecen los clientes que no tengan estén especificados en ninguna venta, ni las ventas que no tengan especificado algún cliente.

Composición externa justify, RIGHT y FULL JOIN

La composición externa se expresa de manera similar al INNER JOIN ya que de igual forma se necesita una condición de combinación pero esta añade al resultado las filas que no cumplan con la condición necesariamente.

Ahora lo importante es notar el uso de las palabras LEFT y RIGHT ya que son las que indicarán la tabla de la cual se van a añadir las filas sin correspondencia. Es decir, si ponemos LEFT nos traerá todas las filas de la izquierda y si ponemos RIGHT las de la derecha. Mientras que con la palabra FULL llenamos todas nuestras bases y nos regresa como resultado todas las filas aunque no tengan correspondencia de un lado o de otro.

Para poder identificar de mejor manera las diferencias entre los tres tipos de composiciones externas, veremos el mismo ejemplo en los tres casos pero con la palabra determinante de la composición externa cambiada.

SELECT c.ID,c.FechaEmision, p.Nombre,p.Direccion,p.Estado
FROM Compra AS c
LEFT JOIN Prov AS p
ON p.Proveedor = c.Proveedor

LEFT, RIGHT y FULL JOIN en SQL

La consulta nos trae de resultado muchas filas entre las cuales incluye las de la tabla compra (izquierda) que no tienen necesariamente un referente en la tabla proveedor.

SELECT c.ID,c.FechaEmision, p.Nombre,p.Direccion,p.Estado
FROM Compra AS c
RIGHT JOIN Prov AS p
ON p.Proveedor = c.Proveedor

Composición externa LEFT, RIGHT y FULL JOIN en SQL

Este resultado es aún más claro que el anterior ya que trae todas las filas de la tabla proveedor (derecha), incluso si no tienen en correspondencia algo en la tabla compra.

SELECT c.ID,c.FechaEmision, p.Nombre,p.Direccion,p.Estado
FROM Compra AS c
FULL JOIN Prov AS p
ON p.Proveedor = c.Proveedor

Composición externa LEFT, RIGHT y FULL JOIN en SQL

En este último caso el resultado es el mismo que el anterior y lo podemos comprobar con el número de filas resultado. Pasa esto ya que todas las opciones disponibles se dan con un RIGHT JOIN y no hay nada exclusivo del LEFT JOIN. Si hubiera habido filas que tuviesen solo registros concordantes del lado izquierdo, el resultado del FULL JOIN hubiese sido más claro al juntar todos los resultados posibles.

Para finalizar con la nota me gustaría mencionar algunos consejos al momento de seleccionar el tipo de JOIN a utilizar, primeramente se debe hacer un JOIN normal con la debida condición de comparación para combinar filas y luego deberías pensar si lo que necesitamos es una composición externa o interna, en cuyo caso lo mejor sería seguir estas normas:

  • Si pueden haber filas de la primera tabla que no estén  relacionadas con filas de la segunda tabla y nos interesa que salgan en el resultado, entonces cambiamos a LEFT JOIN.
  • Si pueden haber filas de la segunda tabla que no estén  relacionadas con filas de la primera tabla y nos interesa que salgan en el resultado, entonces cambiamos a RIGHT JOIN.
  • Si necesitamos LEFT y RIGHT entonces utilizamos FULL JOIN.

Esto sería todo por la lección de esta nota, en la siguiente entrada de SQL básico trataremos el tema de las consultas resumen, las distintas funciones que se utilizan para este tipo de consultas como COUNT o AVG y las cláusulas HAVING y GROUP BY.

Si tienen alguna duda respecto al tema tratado o incluso de SQL básico en general, no duden en preguntar en los comentarios.

¡Nos leemos hasta otra!

 

Referencias

https://www.w3schools.com/SQl
Documentación sobre SQL de Microsoft: https://docs.microsoft.com/en-us/sql

Fuentes de las imágenes:

Screenshots tomadas por la autora en SQL Server.
Imagen original usada para la imagen destacada: https://blog.udemy.com/wp-content/uploads/2014/04/shutterstock_113180047.jpg

SQL básico: consultas simples

Bienvenidos a la segunda nota en esta serie de lecciones sobre SQL nivel básico, en esta ocasión nos dedicaremos a hablar un poco sobre las consultas simples que se pueden realizar para obtener datos de tablas o seleccionar registros por ejemplo.

Como vimos en la nota anterior, una de las sentencias más usadas y complejas es SELECT, el resultado de esta es una tabla lógica que alberga las filas resultantes de la ejecución de la sentencia.

Si bien su sintaxis puede llegar a ser muy extensa, por esta nota nos limitaremos a una versión muy simple de ella:

SELECT [ALL|DISTINCT][TOP expresion [PERCENT] [WITH TIES]] 
<lista_seleccion> FROM <origen> [WHERE  <condicion_ busqueda> ]
[ORDER BY {expression_columna|posicion_columna [ASC|DESC]}[ ,...n]]

 

En la nota anterior vimos algunos ejemplos básicos del uso del SELECT y por tanto notamos como se usa el FROM para definir el origen de los datos que queremos recuperar.

Por otro lado tenemos la lista de selección en donde se indican las columnas que se visualizarán en el resultado de la consulta, estas deben estar separadas por comas y aparecerán en el orden en que fueron escritas.

Entonces para poner un ejemplo rápido podríamos utilizar la siguiente sentencia:

SELECT Nombre, Tipo, Categoria
FROM Agente;

 

Lo que nos traerá dichas columnas en ese orden de la tabla agente:

Consulta de SQL

El proceso lógico de una consulta

Un SELECT está compuesto de una combinación de elementos obligatorios y opcionales. No todos los elementos estarán presentes en cada consulta SELECT, sin embargo, cuando un elemento está presente, siempre será evaluado en el mismo orden con respecto a los otros elementos presentes. Por ejemplo, una cláusula WHERE siempre será evaluada después de la cláusula FROM y antes de una cláusula GROUP BY, si es que existe.

A continuación tenemos los elementos ordenamos como se escribirían en una consulta, los números a su izquierda hacen referencia a la prioridad en que dichos elementos  son evaluados:

5: SELECT

1: FROM

2: WHERE

3: GROUP BY Si existe

4: HAVING Si existe

5: ORDER BY

Por lo que brevemente explicado quedaría como sigue:

  1. La cláusula FROM se evalúa en primer lugar, esto para proporcionar el origen del  resto de la declaración. Una tabla virtual se crea y continuamos con el siguiente paso.
  2. La cláusula WHERE es la siguiente a evaluar, filtra las filas de la tabla de origen para que coincidan con un predicado. La tabla virtual filtrada se usará en el siguiente paso.
  3. GROUP BY es el siguiente, se organizan las filas de la tabla virtual de acuerdo a los valores únicos que se encuentran en la lista GROUP BY. Se crea una nueva tabla virtual, que contiene la lista de grupos y continuamos con lo siguiente.
  4. La cláusula HAVING se evalúa a continuación, se filtran los grupos en base a su predicado. La tabla virtual creada en el paso 3 se filtra y continuamos con el paso final.
  5. La cláusula SELECT finalmente se ejecuta, siendo la determinación de qué columnas aparecerán en los resultados de la consulta.

No en todas las consultas es necesario usar todas las cláusulas, por lo que de faltar alguna, simplemente se saltaría el paso y continuaría con las cláusulas existentes.

Alias de columna y de tabla

En el encabezado de cada columna del resultado de la consulta, predeterminadamente aparece el nombre de la columna origen pero en ocasiones es necesario cambiar dicho nombre para una mejor presentación de los datos. De tal manera puedes identificar y leer el resultado de tu consulta de manera sencilla.

El alias de una columna se puede indicar con la cláusula AS, dependiendo de la versión y el sistema gestor que se utilice se tendrán que usar comillas simples para definir el alias o simplemente escribirlo como un texto normal siguiendo las normas de los identificadores.

Un ejemplo utilizando alias en columnas:

SELECT Articulo AS 'ID',Descripcion1 AS 'Descripción', 
Impuesto1 AS 'IVA', PrecioLista AS 'Precio en Lista'
FROM Art;

 

El resultado sería el siguiente:

Consulta SQL

Mientras que la misma consulta sin usar alias quedaría de la siguiente forma:

Consulta SQL - Alias de columna y de tabla

De la misma manera, podemos utilizar algún alias con nuestras tablas, así al momento de unirlas será más sencillo hacer referencias a campos de tablas específicas (eso se verá en el tema de consultas multitabla).

Los alias hacen que las consultas sean más legibles para el programador por lo que es recomendable utilizarlas incluso si se trata de una sola tabla para hacer un buen hábito de ello.

Un ejemplo utilizando una tabla con un alias es como sigue:

SELECT Cliente, Nombre
FROM cte AS c;

 

Se recomienda que se utilicen alias cortos, por lo regular de una sola letra que sería la inicial de la tabla o si se trata de un nombre de tabla formado de distintas palabras que sea lo más corto pero entendible posible. Cabe mencionar que este alias no influye en el resultado de la consulta y que tampoco cambia el nombre original de la tabla.

Funciones

Existen distintas funciones que podemos utilizar en nuestra lista de selección o en otras cláusulas de nuestra consulta como el WHERE.

Los principales tipos de funciones son de fecha, cadena, numéricas, de conversión y otras avanzadas.

Para evitar alargar mucho esta nota, les dejaré un enlace con una lista específica de manera que puedan buscar alguna función que se ajuste a sus necesidades. Cabe mencionar que este listado es para quienes usan SQL Server pero hay algunas similitudes por ejemplo para quienes utilizan MySQL.

Listado de funciones

Columnas Calculadas

A menudo es necesario utilizar operaciones con los datos de nuestras tablas para obtener resultados determinantes y específicos que no se podrían obtener con solo los datos de las tablas como tal .

Esto se evalúa a partir de una expresión que puede contener cualquier operador válido(+, -, *, /…), función válida, constantes, parámetros e incluso combinar varias operaciones de ser necesario.

Para que quede aún más claro cómo podemos utilizar esto, les dejo un ejemplo de una consulta con una columna calculada, utilizando a la vez un alias para la misma:

SELECT p.Nombre+' '+p.ApellidoPaterno+
' '+p.ApellidoMaterno AS 'Nombre Completo',
DATENAME(dw,p.FechaNacimiento)+','+
' '+DATENAME(dd,p.FechaNacimiento)+
' '+DATENAME(mm,p.FechaNacimiento)+
' '+DATENAME(yyyy,p.FechaNacimiento) AS 'Fecha de Nacimiento'
FROM Personal AS p;

 

Antes de que vean el resultado, quisiera explicarles un poco la consulta.

La primer columna se trata de una suma de 3 campos, además de esto entre cada uno se le agrega un espacio en blanco para que se lea de manera adecuada.

La segunda columna está conformada de otra suma pero utilizando además una función de fecha. La función DATENAME obtiene del campo seleccionado el nombre según lo que se le indique en su primer parámetro, siendo en este caso “dw” utilizado para el día de la semana, “dd” para el día en numérico, “mm” para el mes y finalmente “yyyy” para indicar el año a cuatro dígitos.

Ahora bien, el resultado sería como sigue:

Consulta SQL - Columnas calculadas

Ordenación de filas del resultado con cláusula ORDER BY

Si por alguna razón necesitamos que nuestros resultados sean mostrados en un orden específico lo podemos hacer mediante la cláusula ORDER BY.

Se puede indicar desde una columna o varias separadas por una coma, la columna que se quiera seleccionar deberá ser indicada mediante el nombre de columna en su origen de datos.

Se debe saber que por defecto se ordenan los datos de manera ascendente (ASC) pero de así necesitarlo le podemos indicar que sea de forma descendente (DESC).

Algunas cosas que debemos tener en cuenta al utilizar esta cláusula:

  • Si la columna de ordenación es numérica, las filas se ordenarán de menor a mayor.
  • Si la columna de ordenación es alfanumérica, las filas se ordenarán por orden alfabético.
  • Si la columna de ordenación es de tipo fecha, las filas se ordenarán de más antigua a más reciente o futura.

Ahora veamos un ejemplo utilizando ORDER BY:

SELECT Mov AS 'Movimiento', Cliente, Importe
FROM Venta AS v
ORDER BY Importe;

 

El resultado es ordenado entonces por el importe, de la venta que tenga el menor a la venta que tenga el mayor importe.

Consulta SQL - Order By

Selección de filas con la cláusula WHERE

Esta cláusula se usa para determinar las filas que se desean utilizar del origen de datos en específico. Es una cláusula muy utilizada ya que en el resultado de la consulta solo se mostrarán las filas que cumplan con que la condición dada sea TRUE.

Se pueden utilizar condiciones simples o más elaboradas con varios predicados unidos por operadores como AND u OR. Si se planea utilizar más de un predicado en la condición unidos con operadores se recomienda poner cada predicado entre paréntesis para evitar confusiones.

Los predicados que se trabajan en SQL son los siguientes:

  • Comparación estándar ( =  , <> , !=, < , <= , !<, >  , >= ,!>)
  • Pertenencia a un intervalo (BETWEEN)
  • Pertenencia a un conjunto (IN)
  • Test de valor nulo (IS NULL).
  • Coincidencia con patrón (LIKE)
  • Si contiene (CONTAINS)
  • FREETEXT

Un ejemplo del uso de la cláusula WHERE con una comparación estándar sería como sigue:

SELECT Personal, Nombre, ApellidoPaterno AS 'Apellido Paterno',
YEAR(FechaNacimiento) AS 'Año de Nacimiento'
FROM Personal WHERE YEAR(FechaNacimiento)  < 1980;

 

Donde la consulta pide algunas columnas de la tabla Personal pero solo de los empleados que hayan nacido antes de 1980.

Consulta SQL - WHERE

Podemos utilizar el predicado BETWEEN para definir un rango de datos entre dos expresiones, vamos directamente a un ejemplo.

SELECT Mov AS 'Movimiento', MovID AS 'ID del Movimiento',
DATENAME(dd,FechaEmision)+' '+ DATENAME(mm,FechaEmision)+
' '+DATENAME(yyyy,FechaEmision) AS 'Fecha de Emisión'
FROM Venta AS v
WHERE FechaEmision BETWEEN '20090101' AND '20090131'
ORDER BY FechaEmision;

 

En este ejemplo estamos requiriendo ciertas columnas de la tabla Venta donde la fecha de emisión de los movimientos sean las del mes de enero del año 2009, ordenadas por la fecha de emisión misma.

Consulta SQL - BETWEEN

Ahora bien, sigamos con el predicado IN, esta evalúa si el valor de la expresión es uno de los valores incluidos en la lista de valores designados entre paréntesis en el predicado. Podemos utilizar cualquier expresión siempre y cuando se utilice el mismo tipo de datos entre expresiones.

SELECT Mov AS 'Movimiento', MovID AS 'ID Movimiento',Proveedor
FROM Compra AS c
WHERE Proveedor IN ('PROVE001', 'PROVE002');

 

En esta consulta buscamos que se nos muestren los movimientos de compras hechos por los proveedores PROVE001 y PROVE002.

Consultas SQL - WHERE

Uno de los predicados más útiles en caso de que nuestra base de datos tengan muchos datos nulos es el de IS NOT NULL. Que básicamente se trata de obtener los datos de las columnas que no sean nulos pero con el ejemplo quedará más claro el objetivo de hacer esto.

Utilizando parte de una consulta anterior, la base de datos que estoy utilizando tiene varios valores nulos por lo que en la consulta de la cláusula ORDER BY utilice este predicado como sigue:

SELECT Mov AS 'Movimiento', Cliente, Importe
FROM Venta AS v
WHERE Importe IS NOT NULL
ORDER BY Importe;

 

Esto hace que si hay valores nulos, no nos los muestre para realmente obtener los datos que necesitamos, de lo contrario la consulta sin el IS NOT NULL nos daría este resultado:

Consultas SQL - IS NOT NULL

Finalmente podemos utilizar operadores lógicos para unir más de una condición simple y conformar una condición compuesta.

SELECT Personal, Nombre, ApellidoPaterno, Tipo, Estado
FROM Personal
WHERE (Nombre LIKE 'J%') AND (Estado='DISTRITO FEDERAL');

 

En esta consulta estamos utilizando un predicado que no hemos mencionado y es el LIKE. Este predicado busca similitudes dependiendo lo que le indiques, en este caso se busca que el nombre de empleado empiece con la letra “J”, por otro lado y como segunda condición se busca que el empleado sea del estado “Distrito Federal”.

Consultas SQL

Con esto finalizamos la lección de la nota de hoy no sin antes recordarles que esto se trata de un tipo de serie por lo que este pequeño curso de SQL básico continuará en futuras entradas, cualquier duda que pudieran tener son bienvenidos de dejarlas en los comentarios y yo responderé a la brevedad posible.

En la siguiente entrada de SQL básico trataremos el tema de las consultas multitabla, todas las maneras en las que podemos juntar dos o más tablas para obtener resultados totalmente distintos a los que obtenemos utilizando una sola tabla.

¡Nos leemos hasta otra!

Referencias

https://www.w3schools.com/SQl

Documentación sobre SQL de Microsoft: https://docs.microsoft.com/en-us/sql

Imágenes usadas:

Logo SQL:http://sanalkurs.net/etiketler/sql-injection

Screenshots tomadas por la autora en SQL Server.

SQL Básico: conceptos básicos

SQL (Structured Query Language) o en español Lenguaje Estructurado de Consulta es el lenguaje utilizado para definir, controlar y acceder a los datos almacenados en una base de datos relacional.

Se trata de un lenguaje universal empleado en cualquier sistema gestor de bases de datos relacional, cuenta con un estándar definido a partir del cual cada sistema gestor ha desarrollado una versión propia.

Sigue leyendo