Vista previa del material en texto
Clase 15 - Lenguaje Estructurado de Consulta SQL. Parte 2 Introducción En la clase anterior pudimos ver los comandos de consultas básicas y de selección (SELECT) simple con SQL. En esta clase veremos otros tipos de comandos, y consultas más complejas. Inserción de filas en una tabla La inserción de nuevos registros se realiza con la sentencia INSERT INTO especificando la tabla en la que se insertará y las columnas para las cuales se insertarán valores. Por ejemplo, para agregar un nuevo producto a la tabla Products INSERT INTO Products (ProductID, ProductName, SupplierID, CategoryID, Unit, Price) VALUES (78, 'Queso Crema', 2, 4, '1 kg pkg.', 30); Es preciso notar que siempre debe existir correspondencia entre el orden de las columnas que se y los respectivos valores que se proveen. Por ejemplo, el ejecutar INSERT INTO Products (ProductID, ProductName, SupplierID, CategoryID, Unit, Price) VALUES ('Queso Crema', 78, 2, 4, '1 kg pkg.', 30); resultará en un error ya que se está intentando registrar un dato de tipo CHAR en la clave primaria de la tabla. Modificación de filas de una tabla La modificación de registros se realiza con la sentencia UPDATE, explicitando los nuevos valores para los campos que se quieren actualizar. Por ejemplo, para actualizar el precio del producto número 5 se puede ejecutar UPDATE Products SET Price = 100 WHERE ProductID = 5 Puede verificar que efectivamente se haya actualizado el precio ejecutando SELECT * FROM Products WHERE ProductID = 5; _________________________________________________________________________ Importante: Si no se provee ninguna cláusula WHERE, la modificación se realizará para todos las filas de la tabla sobreescribiendo los valores previos. Por lo tanto, procure siempre incluir la correspondiente cláusula WHERE cuando modifique registros. _________________________________________________________________________ UPDATE Products SET Price = 100; SELECT * FROM Products; Borrado de filas de una tabla El borrado de registros se realiza con la sentencia DELETE FROM acompañada de la correspondiente sentencia WHERE. DELETE FROM Products WHERE ProductID >= 5; SELECT * FROM Products; _________________________________________________________________________ Importante: Al igual que con la sentencia UPDATE, se eliminarán todas las filas de la tabla si no se provee ninguna cláusula WHERE. _________________________________________________________________________ Consultas de mayor complejidad En ocasiones se requiere involucrar múltiples tablas en una misma consulta. Por ejemplo, si se quisiera mostrar todos los productos con su correspondiente nombre y descripción de categoría habría que reunir la información contenida en la tabla Products con la contenida en la tabla Categories. Esta vinculación puede realizarse con la sentencia JOIN, que permite reunir tablas por medio de la clave foránea. SELECT p.ProductName, p.CategoryId, c.CategoryName, c.Description FROM Products AS p JOIN Categories AS c ON p.CategoryID = c.CategoryID; La condición de reunión se especifica en la cláusula ON, en donde se expresa que se requiere reunir las tablas haciendo coincidir la clave foránea de categoría de la tabla Products con la clave primaria de la tabla Category. Esta cláusula no debe olvidarse, ya que en caso de no especificar ninguna condición de reunión se obtendrán resultados inválidos. Pruebe ejecutar SELECT p.ProductName, p.CategoryId, c.CategoryName, c.Description FROM Products AS p JOIN Categories AS c; y observe que la primera y segunda fila tienen el mismo CategoryID pero sin embargo el CategoryName es diferente. Al no especificar condición de reunión, la base de datos está emparejando cada fila de la primera tabla con cada fila de la segunda, de manera que no refleja fielmente la relación de la información almacenada. Ordenar el resultado de una consulta Es posible ordenar el resultado de una consulta según una determinada columna por medio de la cláusula ORDER BY, como se muestra a continuación SELECT * FROM Products ORDER BY Price; Por defecto, se ordenarán las filas de manera ascendente, es decir, los productos con menor precio aparecerán primero en la lista. Puede especificar lo contrario con SELECT * FROM Products ORDER BY Price DESC; que indica un ordenamiento descendente, de manera que los productos más caros aparecerán primeros. Agregaciones Las agregaciones permiten calcular medidas resúmen acerca de los datos contenidos en una determinada columna. Por ejemplo, para calcular el precio máximo, mínimo y promedio de los productos registrados puede ejecutar SELECT MIN(Price) AS PrecioMínimo, MAX(Price) AS PrecioMáximo, AVG(Price) AS PromedioDePrecios FROM Products; Por otro lado, con la agregación COUNT es posible contar la cantidad de productos registrados de una determinada categoría SELECT COUNT(ProductID) AS CantidadDeProductosCategoría8 FROM Products WHERE CategoryID = 8; o bien calcular el total de precios de los productos con categoría 8 con la agregación SUM SELECT SUM(Price) AS TotalPreciosProductosCategoría8 FROM Products WHERE CategoryID = 8; Agrupaciones Las agrupaciones permiten formar grupos de acuerdo a una determinada columna, y calcular medidas resúmen sobre ellos. Por ejemplo, es posible obtener un listado que muestre el precio promedio de los productos según su categoría. Para ello, se deberán agrupar todos los productos con la misma categoría y calcular el promedio de precio en cada categoría por separado. Esto se realiza con la sentencia GROUP BY, seguida de las columnas incluidas en SELECT pero que no son agregaciones, tal como se muestra a continuación SELECT CategoryID, AVG(Price) FROM Products GROUP BY CategoryID; También es posible aplicar filtros a cada uno de los grupos con la sentencia HAVING. Se debe tener en cuenta que este tipo de filtro es a nivel de grupo, es decir, es el grupo entero de registros el que se evalúa si cumple la condición o no y por tanto no deben con los filtros a nivel de fila que se realizan en la cláusula WHERE. Por ejemplo, es posible obtener un listado de aquellas categorías de productos cuyo precio promedio es menor o igual a 25 SELECT CategoryID, AVG(Price) AS PromedioPrecio FROM Products GROUP BY CategoryID HAVING PromedioPrecio <= 25; Para ordenar los promedios de precios de menor a mayor, se debe incluir la sentencia ORDER BY siempre al final de la consulta SELECT CategoryID, AVG(Price) AS PromedioPrecio FROM Products GROUP BY CategoryID HAVING PromedioPrecio <= 25 ORDER BY PromedioPrecio; La cláusula HAVING también acepta múltiples condiciones de grupo, unidas mediante los operadores AND o OR según corresponda SELECT CategoryID, AVG(Price) AS PromedioPrecio, MIN(Price) as PrecioMinimo FROM Products GROUP BY CategoryID HAVING PromedioPrecio <= 25 AND PrecioMinimo > 6; En ocasiones se requiere formar grupos únicamente considerando algún subconjunto de filas de la tabla. Para ello, se debe incluir el filtro a nivel de fila con la cláusula WHERE justo antes de la cláusula GROUP BY. Por ejemplo, para generar un listado con el promedio de precios según categoría para los productos provistos por el proveedor 1: SELECT CategoryID, AVG(Price) AS PromedioPrecio FROM Products WHERE SupplierID = 1 GROUP BY CategoryID ORDER BY PromedioPrecio; Lenguaje de Control de Transacciones (TCL) Las sentencias INSERT, UPDATE y DELETE vistas hasta el momento realizaban modificaciones en la base de datos de manera directa. Una vez ejecutada alguna de ellas, no era posible revertir la operación para recuperar los valores anteriores, lo que significaba que los cambios se realizaban de manera definitiva. Esta forma de operar trae consigo algunos inconvenientes, ya que no resguarda a la base de datos de los posibles fallos que puedan ocurrir durante la ejecución de las operaciones, pudiendo provocar así inconsistencias en la información almacenada. Para ilustrar , considere la base de datos de un banco. Suponga que Matias realiza una transferencia de $10.000 hacia Esteban.Pensándolo en términos de operaciones en la base de datos, esta transferencia debería reflejarse en la misma como una reducción de $10.000 en el monto de la cuenta corriente de Matías y un incremento de igual monto en la cuenta corriente de Esteban. Realizar esto implica ejecutar dos operaciones UPDATE en los registros de una misma tabla, para lo cual se deben realizar dos sentencias SQL distintas: una que disminuya el saldo de Matías y otra que aumente el saldo de Esteban. Ahora bien, ¿qué sucedería si, por algún fallo en el servidor, se ejecuta la primera sentencia UPDATE pero la segunda no?. Ciertamente, esto implica una pérdida en la integridad de los datos ya que tras el fallo, el flujo neto de saldos del banco es distinto de cero. Matías vería un débito en su cuenta pero Esteban no obtendría el correspondiente aumento de fondos. Transacciones En situaciones como la descrita, es de suma importancia que una serie de operaciones de DML se ejecuten conjuntamente como si se tratase de una única operación. Para ello es que se implementan las transacciones. En SQL, una transacción consta de un conjunto de operaciones que se ejecutan como si se tratase de una unidad lógica de trabajo. Para indicar que el comienzo de una transacción, se debe ejecutar START TRANSACTION; A continuación, todas las sentencias SQL que se ejecuten quedarán grabadas temporalmente pero no se realizarán modificaciones físicas en la base de datos hasta que el usuario confirme la transacción. Durante una transacción, podemos pensar que estamos trabajando con una base de datos “virtual” la cual es una copia de la base de datos original. Para confirmar una transacción exitosa se ejecuta COMMIT; que indica que finalmente se pueden escribir de manera definitiva los cambios realizados. Si en medio de transacción ocurre un fallo, el usuario puede abortar la transacción y volver al estado previo de la base de datos ejecutando ROLLBACK; La gran mayoría de los sistemas de bases de datos soportan el manejo de transacciones, sin embargo, lamentablemente nuestra base de datos de ejemplo de W3Schools tiene deshabilitada este tipo de operaciones. De todas formas, se desarrollará a continuación un ejemplo utilizando el modelo de datos de la misma. Ejemplo: registrando una venta Considere que se debe registrar una venta con su detalle, el cual consta de un conjunto de productos y su respectiva cantidad comprada. En este caso es preciso utilizar una transacción dado que ante un fallo en el registro del detalle quedará reflejada en la base de datos una orden vacía o incompleta. La base de datos del ejemplo contiene las tablas Orders y OrderDetails, en donde esta última posee una clave foránea en la columna OrderID que apunta a la columna OrderID de la tabla Orders. Dado que OrderDetails referencia a una fila de Orders, deberá primero registrarse la orden y posteriormente su respectivo detalle. En primer lugar se deberá iniciar la transacción con START TRANSACTION; enseguida se deberá registrar la orden INSERT INTO Orders (CustomerID, EmployeeID, OrderDate, ShipperID) VALUES (15, 5, '2021-01-01', 2); Hasta este momento, la nueva orden se encontraría escrita de manera temporal en la tabla virtual. Si la herramienta lo permitiese, sería posible verificar esto realizando un SELECT sobre la tabla Orders SELECT * FROM Orders WHERE OrderDate = '2021-01-01'; lo cual retornaría la fila recientemente insertada. Si en este momento se aborta la transacción ejecutando ROLLBACK; se vería que el INSERT no se ha escrito de manera definitiva, dado al volver a consultar la tabla con la sentencia SELECT * FROM Orders WHERE OrderDate > '2021-01-01'; no retornaría ningún resultado. Ahora bien, para simular un registro completo de la venta, habría que ejecutar el siguiente conjunto de sentencias finalizando con la confirmación de la transacción START TRANSACTION; INSERT INTO Orders (CustomerID, EmployeeID, OrderDate, ShipperID) VALUES (15, 5, '2021-01-01', 2); INSERT INTO OrderDetails (OrderID, ProductID, Quantity) VALUES (10444, 11, 5), (10444, 12, 10), (10444, 2, 80); COMMIT; Tras ejecutar COMMIT, ambas inserciones quedarían asentadas de manera definitiva en la base de datos y a partir de aquí se podría verificar esto ejecutando SELECT o.OrderID, o.OrderDate, od.OrderDetailID, od.ProductID, od.Quantity FROM Orders AS o JOIN OrderDetails AS od ON o.OrderID = od.OrderID WHERE o.OrderDate = '2021-01-01';