Práticas de Bases de Datos o 2 de Sistemas Juan David González Cobas Fernando Cano Espinosa Curso 2010-2011 1. TOMA DE CONTACTO 1. Virtualización. Para el desarrollo de las prácticas de Bases de Datos vamos a utilizar un gestor llamado Postgres. Lo haremos sobre Ubuntu, una distribución de linux, que correrá como una máquina virtual. El software de virtualización es VirtualBox, recientemente adquirido por Oracle. Por tanto, para comenzar las prácticas, desde Windows y con vuestra cuenta autenticada por Ident, ejecutáis VirtualBox y arrancáis la máquina virtual denominada Bases de Datos. Una vez hecho esto se os comunicará el nombre de usuario y clave, para poder empezar a utilizar Ubuntu. 2. Qué es postgreSQL. De forma sencilla diremos que postgreSQL es un gestor de bases de datos. Es una ’aplicación’ que nos permite entre otras muchas cosas definir y manipular bases de datos. Se trata de sofware libre que corre sobre Linux-Unix aunque exixten versiones para otros sistemas operativos. Como gestor es posiblemente uno de los que incluye un SQL (lenguaje con el que realizaremos nuestras operaciones) más estándar. Para manejar postgreSQL disponemos, entre otras muchas opciones, de un intérprete de comandos (un terminal-based front-end), el psql, con el que podremos trabajar en modo texto. Existen otras aplicaciones en modo gráfico como pgadmin3 que podremos utilizar en alguna ocasión. También es posible trabajar con prostgreSQL a través de la web con php. Nosotros vamos a trabajar con la más sencilla. Dependiendo del ordenador que nos ha tocado es posible que no dispongamos de un servidor postgres bien configurado. Para ello será necesario seguir las instrucciones del documento configuracion_postgres.pdf que se encontrará en la página web de las prácticas. 3. Ejecutar psql. Para empezar cada alumno debería tener su propia cuenta en Linux. Una vez iniciada la sesión abrimos un terminal y vamos a ejecutar la siguiente secuencia de comandos. Ejecutamos el psql de la forma $ psql -U username base_de_datos NOTA: en lo que sigue vamos a utilizar como username un usuario de la base de datos que debe estar creado en cada máquina y que tiene como nombre ’alumno’ y de password ’alumna’. En el caso del parámetro base_de_datos, se trata de una base de datos que ya exista. En una instalación normal de prostgreSQL existe una base de datos de nombre template1 a la que siempre es posible conectarnos. Por lo tanto nuestra orden será: $ psql -U alumno template1 Una vez en el intérprete veremos el prompt template1=> que nos indica que la base de datos a la que estamos conectados es template1. Se trata de una base de datos patrón que no vamos a utilizar. El símbolo >significa que no somos superusuarios de postgreSQL, en otro caso se nos mostrará el símbolo #. 4. Un rápido ejemplo. Ahora, sin salir del psql vamos a crear nuestra propia base de datos mediante la orden template1=> create database miprueba; No olvidar el ’;’ para finalizar la orden. Ya tenemos creada nuestra base de datos con nuestro nombre de usuario. Si lo que queremos ahora es conectarnos a ella debemos ejecutar template1=> \c miprueba; En este momento el prompt debe indicar el nombre de nuestra base de datos, que se encuentra vacía. Vamos a crear una tabla t1 con dos columnas id , nombre una de tipo entero y otra de tipo cadena. Ejecutaremos miprueba=> create table t1 (id int, nombre varchar(10)); 1 Como la tabla está vacía vamos a insertar un par de filas: miprueba=> insert into t1 values (1, ’Ana’); miprueba=> insert into t1 values (2, ’Luis’); Comprobamos que el contenido de la tabla es el que esperamos. miprueba=> select id, nombre from t1; Ante un imprevisto cambio de sexo vamos a modificar nuestros datos de la forma: miprueba=> update t1 set nombre=’Luisa’ where id=2; Ahora, ahítos de alegría, volvemos a dejar las cosas como al principio y salimos ejecutando la secuencia de órdenes: miprueba=> delete from t1; miprueba=> drop table t1; miprueba=> \c template1 template1=> drop database miprueba; template1=> \q 5. El entorno de trabajo. El intérprete de comandos psql puede parecer un poco incómodo para aquellos que no estén acostumbrados a trabajar con este tipo de entornos, por ello vamos a ver un par de detalles para agilizar nuestro trabajo. Como muchas órdenes pueden ocupar más que una línea, es normal hacer uso de un editor. Podemos definir el editor que preferimos (kwrite, kate, vi, emacs, gedit, joe, pico, etc.), para ello desde la línea de comandos de Linux, ejecutaremos la orden $ export PSQL_EDITOR=mieditor_preferido Si entramos de nuevo en el intérprete psql, nos daremos cuenta que con las flechas del cursor podemos recuperar las órdenes que dimos anteriormente; psql guarda un histórico de nuestras órdenes por lo que fácimente podemos volver a crear nuesta base de datos miprueba. Podemos optar por no usar un editor y trabajar con múltiples lineas de tal forma que mientras que no finalicemos la orden, el intérprete nos presentará un prompt diferente en la sucesivas líneas. Por ejemplo si queremos insertar un cliente nuevo, haríamos miprueba=> insert miprueba-> into t1 miprueba-> values (3, miprueba(> ’Pedrito miprueba’> ’ miprueba(> ) miprueba-> ; Observar que el prompt va cambiando dependiendo del estado en que que nos encontremos ( =>, ->, ’>) y que la orden se finaliza con el ’;’. Cada orden se guarda en un buffer, que se llama el ’query buffer’. Si invocamos al editor nos permitirá modificar la última orden. Para hacer esto tan sólo tendremos que ejecutar \edit o sencillamente \e. Al salir del editor el intérprete ejecutará lo que hemos escrito. Invocar al editor y veréis el insert anterior, y podéis insertar otro cliente. Otra alternativa, posiblemente más cómoda, sobre todo si lo que queremos es ejecutar un fichero entero de órdenes, consiste en mantener un editor abierto en una ventana, y en otra ventana, tener el intérprete de psql. Supongamos que el fichero que editamos se corresponde con /home/pepe/bdatos/miFicheroDeOrdenes.sql; nos colocamos en el directorio /home/pepe/bdatos, (en psql, con la orden cd , nos podemos cambiar al directorio que especifiquemos); y para ejecutar el fichero bastaría poner en el prompt miprueba=>\i miFicheroDeOrdenes.sql 2 6. Crear la base de datos. En el directorio de prácticas de la asignatura existen varios ficheros que deberíais copiar en un directorio vuestro. Estos son: createPedidos.sql: es un scrpit sql que contiene las órdenes de creación de la base de datos y las tablas. insertPedidos.sql: es un scrpit sql que inserta valores en las tablas. Pedidos.pdf y pedidos_uml.pdf: enunciado, diagrama Entidad Relación y tablas resultantes de la base de datos de pedidos Una vez hecho desde el psql ejecutar las órdenes: template1=> \i createpedidos.sql pedidos=> \i insertpedidos.sql En este momento deberíais tener creada la base de datos e insertados los datos en las tablas y por tanto listos para empezar a realizar consultas. 7. Los metadatos. Ahora vamos a ver algunas órdenes para ir manejándonos. template1=> \l - muestra las base de datos template1=> \dt - muestra las tablas de la actual base de datos template1=> \d tabla - muestra información de la tabla template1=> \du - muestra las usuarios template1=> \e - edita el query-buffer template1=> \i - ejecuta un fichero template1=> \o - redirecciona la salida template1=> \h - muestra la ayuda SQL Ejecutar algunas de estas órdenes. Aún así se recomienda tener a mano, ya sea en papel o en pantalla, los nombres de las tablas y de las columnas. 8. Practicamos. Vamos a conectarnos a la base de datos de pedidos y a ejecutar varias órdenes sencillas sobre la tabla de clientes desde el psql. Para ello se puede editar la orden en la propia línea de comandos o utilizando el editor elegido. a) mostrar toda la tabla select * from clientes; b) mostrar el id_cliente y el nombre de todos los clientes select id_cliente,nombre from clientes; c) mostrar toda la información de los clientes de Gijón (ojo con las mayúsculas y minúsculas) select * from clientes where ciudad= ’GIJON’; d) mostar el id_cliente y el nombre de todos los clientes que sean de Gijón o de Mieres select id_cliente, nombre from clientes where ciudad= ’GIJON’ or ciudad = ’MIERES’; 9. Borrar la base de datos y volver al mismo punto en el que estábamos. Teniendo los scripts de creación e inserción de datos es fácil reconstruir nuestra base de datos de forma rápida. 3 10. Vamos a utilizar la ayuda y ver algunos ejemplos de cómo se expresa la sintaxis de órdenes SQL. Para mostrar la ayuda de postgreSQL abrimos con un navegador el fichero file:///usr/share/doc/postgresql-doc-8.4/html/index.html La sintaxis de las órdenes SQL se encuentra descrita en la P art V I de la documentación. También podemos llegar a ella directamente abriendo el fichero: file:///usr/share/doc/postgresql-doc-8.4/html/reference.html Es posible, no hayamos instalado la documentación, y que no encontremos estos ficheros. Para instalar la documentación bastaría con ejecutar: $ sudo apt-get install postgresql-doc-8.4 pero siempre nos quedará París o la página oficial http://www.postgresql.org Es muy importante familiarizarse con esta notación, ya no solo para aprender SQL. Se trata, más o menos, de una gramática libre de contexto, y lo que se nos muestra son las reglas de producción. En esta notación tenemos que recordar que: Los símbolos terminales (palabras que aparecen tal cual en las órdenes) se expresan en mayúsculas( ej. SELECT ). Los símbolos no terminales (símbolos que producen cadenas de otros símbolos) se expresan en minúsculas (ej. condition, expression) Las barras verticales expresan distintas alternativas. Los corchetes expresan que lo que hay dentro es opcional. Las llaves expresan alguna de las alternativas que se incluyen debe aparecer. Los puntos suspensivos se utilizan para describir una repetición, generalmente separados por comas, del símbolo que se encuentra a la izquierda de los puntos suspensivos. Al aparecer entre corchetes se describe una lista de uno a más elementos. Para cada orden encontraremos generalmente con los siguientes apartados El nombre de la orden. La sintaxis de la orden (synopsis). Una descripción. Una explicación de los parámetros. Unas notas. Unos ejemplos. La compatibilidad con otras versiones de SQL. Algunos aspectos de la sintaxis no son fáciles de encontrar en esta documentación, pero dado que se trata de un SQL bastante estándar cualquier sintaxis nos puede servir para crear consultas sencillas. Aún así es recomendable echar un vistazo al Chapter 7. Queries donde podemos ir desmenuzando la sintaxis del select. 4 11. Seguimos probando algunas órdenes sencillas: Mostrar (SELECT ) los productos cuyo precio esté entre 90 y 120. Disponemos del predicado between para ello. select * from productos where precio between 90 and 120; Insertamos (IN SERT ) un nuevo producto insert into productos (id_producto, nombre, precio) values (7,’KIWI’, 100); Observar que no hemos introducido un valor para existencias. Para ello hemos escrito los nombres (y el orden) de las columnas que vamos a introducir. También podríamos haber utilizado la palabra reservada null para dicha columna. Actualizamos (U P DAT E) las existencias del nuevo producto e incrementamos el precio en un 10 % update productos set existencias= 50, precio = precio * 1.1 where id_producto=7; Borramos (DELET E) el nuevo producto delete from productos where id_producto=7; 5 2. EL SELECT COMO UNA FORMA DE VIDA 1. Nos conectamos a nuestra base de datos de pedidos (crearla si fuera necesario) 2. Es fundamental ver y entender bien el significado del Producto (join) de tablas. Cuando queremos recuperar información que se encuentra repartida entre distintas tablas vamos a basarnos en dos ideas: la primera es la de repetir columnas en diferentes tablas para poder enlazar dicha información y la segunda es utilizar la operación producto cartesiano de conjunto (entendiendo que una tabla en un conjunto de filas). Para realizar el producto cartesiano (cross join) de pedidos con clientes podemos expresarlo así: select * from pedidos , clientes; o de forma algo más elegante así: select * from pedidos cross join clientes; Para ver más claramente lo que hace esta consulta vamos a mostrar las columnas pedidos.id_cliente y clientes.id_cliente select pedidos.id_cliente, clientes.id_cliente from pedidos cross join clientes; Añadimos al from la tabla de empleados y mostramos también pedidos.id_empleado y empleados.id_empleado. Observamos cuántas filas obtenemos. Y nos damos cuenta que equivale al producto de los cardinales de los tres conjuntos de filas (tablas). Estudiar sobre la anterior consulta select * from pedidos cross join clientes; qué condición que debe imponerse a las filas en la cláusula W HERE para obtener un resultado coherente. Modificar esta consulta de forma que no sea necesario imponer la condición en el W HERE. Para ello podemos realizar el producto natural (N AT U RAL JOIN ). O también el IN N ER JOIN que como veremos presenta dos alternativas, una con utilizando la cláusula ON y otra con la cláusula U SIN G. 3. Obtener el id_pedido, el nombre del cliente para todos los pedidos. 4. Obtener el id_pedido, el nombre del cliente y el nombre del empleado para los pedidos atendidos por empleados de Mieres. 5. Obtener el id_pedido, el nombre del cliente y el nombre del empleado para los pedidos realizados el 2 de septiembre de 2006 que han sido atendidos por empleados de Mieres. 6. Introducir las variables de tupla (con el primer objetivo de ahorrarse escribir mucho). 7. Ver cómo se renombran las columnas para distinguir entre el nombre del cliente y del empleado. 8. Obtener el id_pedido, el nombre del cliente, el nombre de los productos y cantidad de los mismos para todos los pedidos. 9. Nombre del cliente y nombre del empleados que comparten ciudad 10. Parejas de nombres de clientes que comparten ciudad 6 3. EL SELECT Y SUS AMIGOS 1. Comparaciones de cadenas (Pattern Matching, LIKE). Para comparar cadenas tenemos el operador LIKE que podemos utilizar en combinación con los caracteres ’ %’ y ’_’ que tienen, respectivamente, el mismo significado que el ’*’ y ’?’ en linux y otros sistemas operativos. Mostrar el identificador y nombre de aquellos productos que cumplen lo siguiente: • empiezan por ’P’ • terminan en ’AS’ • contienen las subcadena ’TA’ En postgreSQL podemos utilizar el ILIKE para que no se distingan mayúsculas de minúsculas (case insensitive). 2. El operador IN (NOT IN ) Mostrar el identificador y nombre de aquellos productos pedidos por ’PEPE’ Mostrar el identificador y nombre de aquellos productos NO pedidos por ’PEPE’ En muchos casos el IN permite hacer intersecciones de conjuntos y el N OT IN nos sirve para la diferencia de conjuntos. 3. Otras comparaciones Mostrar el identificador y nombre de aquellos productos que cuestan más que alguno de los que ha pedido ’PEPE’ 4. El predicado EXISTS Mostrar pedidos con su fecha en los que no aparece ningún producto. 5. La UNION Mostrar los nombres de empleados o clientes que viven en MIERES Añadimos la calle 6. La INTERSECCIÓN (INTERSECT) Mostrar aquellas ciudades donde viven tanto empleados como clientes. 7. El MINUS (EXCEPT) Mostrar aquellas ciudades donde viven empleados pero no clientes. 7 8. El inexistente CON T AIN S El CON T AIN S sirve para implementar la inclusión de conjuntos (⊆), a diferencia del IN (∈) que nos permite comprobar si un elemento pertenece a un conjunto. Por tanto los parámetros de CON T AIN S son dos conjuntos (el resultado de dos select’s). Se suele utilizar en consultas del tipo ’Para todo’. Mostrar los clientes que han pedido todos los productos que ha pedido ’PEPE’. Vamos a definirnos dos conjuntos: A y B. Conjunto A: productos pedidos por cada cliente (c1 ). Conjunto B: productos pedidos por clientes cuyo nombre es PEPE. Si se cumple que B ⊆ A entonces el cliente c1 deber ser mostrado. select nombre from clientes c1 where ( select id_producto -- Conjunto A: productos pedidos por c1 from pedidos p inner join detalles_pedido dp using (id_pedido) where p.id_cliente = c1.id_cliente) contains ( select id_producto -- Conjunto B: productos pedidos por PEPE from clientes c2 inner join pedidos p using (id_cliente) inner join detalles_pedido dp using(id_pedido) where c2.nombre ilike ’PEPE’); Si se observa el predicado contains devuelve TRUE si el conjunto A contiene al conjunto B. Lamentablemente SQL no implementa este predicado y tiene que ser simulado con la condición NOT EXISTS (B MINUS A), es decir NOT EXISTS (B EXCEPT A). Demostramos nuestra capacidad de aprendizaje: mostrar aquellos productos que aparecen en todos los pedidos Insistimos: clientes que han sido atendidos por todos los empleados de Gijón. 9. Algo sobre Valores nulos. Para poder manejar los valores nulos en SQL tenemos que tener en cuenta algunos criterios. Además SQL nos ofrece algunos operadores que nos facilitan el tratamiento de los valores null. En general, las expresiones que incluyen valores nulos devuelven como resultado null. En las expresiones lógicas se siguen las reglas mostradas en las tablas del epígrafe 9.1. Logical Operators. El operador IS (value IS null) nos devuelve true si value contiene el valor null. La expresión CASE nos permite, entre otras cosas, controlar que si un resultado es null podemos cambiarle su valor para poder trabajar mejor con él. Por ejemplo, en algunas expresiones aritméticas es útil convertir valores nulos en ceros (ver 9.12.1). La función COALESCE() también nos permite algo parecido a lo anterior pero de otra forma(ver 9.12.2). La función N U LLIF () es como la función inversa a la anterior (ver 9.12.3). Mostrar los identificadores de pedido y los del empleado, pero si no tiene un empleado asociado mostrar un ’0’. Mostrar los identificadores de pedido y los del empleado, pero si no tiene un empleado asociado mostrar la cadena ’sin asignar’. Mostrar los empleados que no han atendido ningún pedido utilizando el operador NOT IN, ver qué pasa y arreglarlo. 8 10. Empezamos a agrupar (group by, having, count() , max(), etc.) Mostrar el número de pedidos atendidos por ’MARIA’. Identificador del empleado y número de pedidos atendidos por él. Nombre del empleado y número de pedidos atendidos por él. Fecha y número de empleados que han atendido algún pedido en dicha fecha. Identificador de Pedido, fecha del mismo, número de de productos diferentes que incluye y la cantidad media de productos por pedido. Identificador de Pedido y coste total del mismo para aquellos pedidos que incluyen más de dos productos distintos. Para aquellos productos cuyas existencias no cubren las cantidades pedidas, obtener su nombre, sus existencias, las cantidades pedidas y las cantidades necesarias que la empresa tiene que comprar para poder cubrir todos los pedidos. 11. Ordenamos un poco Nombres de productos y el número de pedidos en los que aparecen, ordenados por el nombre alfabéticamente y por el número de pedidos de forma descendente 9 4. Práctica 4: EL SELECT Y LOS JOINS Nombre de los empleados con los identificadores de los pedidos atendidos por ellos Nombre de TODOS los empleados con los identificadores de los pedidos atendidos por ellos Productos y el número de pedidos en los que aparecen Relaciones entre clientes y empleados. Mostrando TODOS los clientes y TODOS los empleados aunque no haya realizado ningún pedido 10