diplomado tics - E-campus :: FCA-UNAM

Anuncio
3.3
Programación de la base de datos
Un sistema manejador de base de datos no es sólo un repositorio estático de
datos,es también un software que nos permite desarrollar programas que
resuelvan necesidades de información y que resguarden la consistencia de la
misma. En esta unidad, abordaremos temas de programación de bases de datos
por lo que necesitaremos un sistema manejador de bases de datos (Database
Management System, DBMS). Te proponemos trabajar con PostgreSQL ya que es
software libre y existen versiones para cualquier sistema operativo 1. El código con
el que ejemplificaremos esta unidad estará basado en este DBMS.
Hay dos actividades preponderantes en el manejo de sistemas de bases de datos.
Por una parte, la recuperación de información, actividad que consiste en
solucionar una necesidad de información del usuario, por ejemplo: ¿cuántas
ventas ha realizado el vendedor Juan Gutiérrez? ¿Cuál es el monto total de venta
por cada cliente? Esta recuperación se hace mediante consultas. En esta unidad,
conocerás las sentencia SELECT, que permite obtener información de manera
versátil.
Por otro lado, el procesamiento de la información, que es una actividad primordial
ya que nos permite manipular los datos de la base con diversos fines. Por ejemplo,
verificar que la información sea almacenada de forma correcta, monitorear quién
está almacenando la información y revisar que se cumplan las políticas de la
empresa antes de aceptar un cambio en los datos. Vas a conocer varias
sentencias para manipular información de una base de datos, todas programadas
con SQL mediante el dialecto de PostgreSQL.
3.3.1. Fundamentos de consultas de base de datos
La instrucción SQL que nos permite consultar información de una o varias tablas
es la instrucción SELECT. Recuerda que el resultado de una consulta siempre
1
Para obtener una versión de PostgreSQL y la documentación necesaria para manejarlo, visita el sitio
http://www.postgresql.org.
serán columnas y renglones. La sintaxis general de uso de esta instrucción es la
siguiente:
Syntax:
SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ]
* | expression [ AS output_name ] [, ...]
[ FROM from_item [, ...] ]
[ WHERE condition ]
[ GROUP BY expression [, ...] ]
[ HAVING condition [, ...] ]
[ { UNION | INTERSECT | EXCEPT } [ ALL ] select ]
[ ORDER BY expression [ ASC | DESC | USING operator ] [, ...] ]
[ LIMIT { count | ALL } ]
[ OFFSET start ]
[ FOR UPDATE [ OF tablename [, ...] ] ]
Si queremos seleccionar todas las columnas y todos los renglones de una tabla
usamos el siguiente comando. Observa el resultado que arroja esta consulta.
TEST=# SELECT * FROM tlibro;
libro_id |
título
| autor_id | tema_id |
sinopsis
----------+---------------------------+----------+---------+----------------------------1 | Cuentos crueles
|
10 |
2 | Todos los fuegos el fuego |
7 |
5 | Serie de cuentos de ficción
5 | Cuentos del siglo XIX
3 | Primero sueño
|
6 |
5 | Poesía colonial
4 | Ficciones
|
11 |
5 | Mejores cuentos de autor
5 | Artificios
|
11 |
5 | Cuentos de J. L. Borges
6 | El llano en llamas
|
|
7 | El túnel
|
|
8 | Los bandidos de Río Frío
|
3 |
5 | Novela naturalista
9 | Clemencia
|
4 |
5 | Inicio del modernismo
|
|
(9 rows)
Para seleccionar algunas columnas usamos la siguiente instrucción.
TEST=# SELECT libro_id, titulo, sinopsis FROM tlibro;
libro_id |
título
|
sinopsis
----------+---------------------------+----------------------------1 | Cuentos crueles
| Cuentos del siglo XIX
2 | Todos los fuegos el fuego | Serie de cuentos de ficción
3 | Primero sueño
| Poesía colonial
4 | Ficciones
| Mejores cuentos de autor
5 | Artificios
| Cuentos de J. L. Borges
6 | El llano en llamas
|
7 | El túnel
|
8 | Los bandidos de Río Frío
| Novela naturalista
9 | Clemencia
(9 rows)
| Inicio del modernismo
Con la instrucción SELECT también es posible seleccionar constantes y
expresiones. Fíjate cómo las columnas del resultado de la consulta pueden tener
un sobrenombre o alias (“ejemplo de suma”).
TEST=# SELECT 2 + 2 AS "ejemplo de suma",
TEST-#
pi() AS "valor de pi",
TEST-#
'Este es un letrero' AS "Letrero";
ejemplo de suma |
valor de pi
|
Letrero
-----------------+------------------+-------------------4 | 3.14159265358979 | Este es un letrero
(1 row)
La cláusula WHERE nos permite determinar qué renglones son parte de la salida y
cuáles no. Si un renglón es parte de la salida, éste debe satisfacer una condición.
El WHERE establece condiciones que son evaluadas como true o false. Por
ejemplo:
Supongamos que deseamos obtener el titulo del libro 7.
TEST=# SELECT titulo FROM tlibro WHERE libro_id
= 7;
título
---------El túnel
(1 row)
Ahora deseamos el título de los libros del autor 11.
TEST=# SELECT titulo FROM tlibro WHERE autor_id = 11;
tíitulo
-----------Ficciones
Artificios
(2 rows)
Obtengamos el nombre del autor del libro 5.
TEST=# SELECT au.nombre
TEST-# FROM tlibro AS li, tautor AS au
TEST-# WHERE li.autor_id = au.autor_id
TEST-# AND li.libro_id = 5;
nombre
-----------Jorge Luis
(1 row)
Este último ejemplo asigna alias a las tablas mediante la partícula AS. De esta
manera puedo usar el alias en lugar del nombre de la tabla.
3.3.2. Consulta de varias tablas
Rescatar registros de una sola tabla es muy inusual. En la realidad, siempre
obtenemos datos de diferentes tablas, a veces de muchas. Es importante
entonces conocer la manera de ‘juntarlas’ para poder obtener valores
almacenados en columnas de unas y de otras tablas. Esto lo realizamos con una
operación relacional llamada Junta o Join.
Existen en general tres tipos de join.
TIPOS
DE
JOIN
Cross join
El resultado es un producto cartesiano, es decir,
una combinación de todos los valores de una
tabla contra todos los valores de otra tabla. No
resulta ser muy útil en la práctica.
Inner join
El resultado es un conjunto de registros que
resultan de la combinación de dos o más tablas
siempre y cuando existan columnas en común y
los valores de dichas columnas coincidan.
Outer join
El resultado es un inner join que además incluye
aquellos valores donde no hay coincidencia en
el origen, ya sea, del lado izquierdo (LEFT
OUTER JOIN) o del lado derecho (RIGHT
OUTER JOIN) o aquellos que no coinciden en
ningún lado (FULL OUTER JOIN).
Figura 3.4. Tipos de join
A continuación unos ejemplos prácticos.
Si quisiéramos obtener los autores de cada libro tendríamos que usar un join
porque los datos están en dos tablas. El título está en la tabla tlibro y el nombre
del autor en la tabla tautor.
TEST=# SELECT li.titulo, au.nombre, au.apellidos
TEST-# FROM tlibro AS li INNER JOIN tautor AS au
TEST-# ON (li.autor_id = au.autor_id);
titulo
|
nombre
| apellidos
---------------------------+----------------+-----------Los bandidos de Río Frío
| Manuel
Clemencia
| Ignacio Manuel | Altamirano
| Payno
Primero sueño
| Sor Juana Inés | de la Cruz
Todos los fuegos el fuego | Julio
| Cortázar
Cuentos crueles
| No
| Recuerdo
Artificios
| Jorge Luis
| Borges
Ficciones
| Jorge Luis
| Borges
(7 rows)
Otra manera de hacer lo mismo sería mediante la cláusula USING, que se
encarga de igualar las columnas que tienen el mismo nombre en las dos tablas.
Utilizamos USING en lugar de ON.
TEST=# SELECT li.titulo, au.nombre, au.apellidos
TEST-# FROM tlibro AS li INNER JOIN tautor AS au
TEST-# USING (autor_id);
titulo
|
nombre
| apellidos
---------------------------+----------------+-----------Los bandidos de Río Frío
| Manuel
| Payno
Clemencia
| Ignacio Manuel | Altamirano
Primero sueño
| Sor Juana Inés | de la Cruz
Todos los fuegos el fuego | Julio
| Cortázar
Cuentos crueles
| No
| Recuerdo
Artificios
| Jorge Luis
| Borges
Ficciones
| Jorge Luis
| Borges
(7 rows)
Como puedes observar, el resultado de estos join no incluye los libros que no
tienen autor. Esto sucede porque el join rescata sólo aquellos registros que
cumplen la condición de igualdad entre columnas en común. Si quisiéramos
rescatar los libros que tiene y los que no tienen autor debemos utilizar un OUTER
JOIN.
TEST=# SELECT li.titulo, au.nombre, au.apellidos
TEST-# FROM tlibro AS li LEFT OUTER JOIN tautor AS au
TEST-# ON (li.autor_id = au.autor_id);
titulo
|
nombre
| apellidos
---------------------------+----------------+-----------Los bandidos de Río Frío
| Manuel
Clemencia
| Ignacio Manuel | Altamirano
| Payno
Primero sueño
| Sor Juana Inés | de la Cruz
Todos los fuegos el fuego | Julio
| Cortázar
Cuentos crueles
| No
| Recuerdo
Artificios
| Jorge Luis
| Borges
Ficciones
| Jorge Luis
| Borges
El llano en llamas
|
|
El túnel
|
|
(9 rows)
Ahora, para obtener los libros con autor más los libros sin autor debemos usar la
siguiente instrucción.
TEST=# SELECT li.titulo, au.nombre, au.apellidos
TEST-# FROM tlibro AS li RIGHT OUTER JOIN tautor AS au
TEST-# ON (li.autor_id = au.autor_id);
titulo
|
nombre
| apellidos
---------------------------+----------------+-----------| Anderson
| Imbert
Los bandidos de Río Frío
| Manuel
| Payno
Clemencia
| Ignacio Manuel | Altamirano
Primero sueño
| Sor Juana Inés | de la Cruz
Todos los fuegos el fuego | Julio
| Cortázar
Cuentos crueles
| No
| Recuerdo
Artificios
| Jorge Luis
| Borges
Ficciones
| Jorge Luis
| Borges
(8 rows)
Combinemos ahora varios tipos de join y obtengamos todos los libros, con autor o
sin autor, indicando el tema al que pertenecen. El número de operaciones join
siempre es igual al número de tablas menos uno.
TEST=# SELECT li.titulo, au.nombre, au.apellidos, te.nombre
TEST-# FROM (tlibro AS li LEFT OUTER JOIN tautor AS au
TEST(# ON (li.autor_id = au.autor_id)
TEST(# LEFT OUTER JOIN ttema AS te
TEST(# ON (li.tema_id = te.tema_id));
titulo
|
nombre
| apellidos
|
nombre
---------------------------+----------------+------------+----------Los bandidos de Río Frío
| Manuel
Clemencia
| Ignacio Manuel | Altamirano | Literatura
| Payno
| Literatura
Primero sueño
| Sor Juana Inés | de la Cruz | Literatura
Todos los fuegos el fuego | Julio
| Cortázar
| Litera tura
Cuentos crueles
| No
| Recuerdo
| Literatura
Artificios
| Jorge Luis
| Borges
| Literatura
Ficciones
| Jorge Luis
| Borges
| Literatura
El llano en llamas
|
|
|
El túnel
|
|
|
(9 rows)
Ahora obtengamos los autores con y sin libro más el tema asociado.
TEST=# SELECT li.titulo, au.nombre, au.apellidos, te.nombre
TEST-# FROM (tlibro AS li RIGHT OUTER JOIN tautor AS au
TEST(# ON (li.autor_id = au.autor_id)
TEST(# LEFT OUTER JOIN ttema AS te
TEST(# ON (li.tema_id = te.tema_id));
titulo
|
nombre
| apellidos
|
nombre
---------------------------+----------------+------------+----------Ficciones
| Jorge Luis
| Borges
| Literatura
Los bandidos de Río Frío
| Manuel
| Payno
| Literatura
Clemencia
| Ignacio Manuel | Altamirano | Literatura
Primero sueño
| Sor Juana Inés | de la Cruz | Literatura
Todos los fuegos el fuego | Julio
| Cortázar
| Literatura
Cuentos crueles
| No
| Recuerdo
| Literatura
Artificios
| Jorge Luis
| Borges
| Literatura
| Anderson
| Imbert
|
(8 rows)
Finalmente, si lo que quisiéramos obtener fuera: todos los libros con y sin autor,
todos los autores, con y sin libro, y los temas asociados, tendríamos que hacer
uso del FULL OUTER JOIN.
TEST=# SELECT li.titulo, au.nombre, au.apellidos, te.nombre
TEST-# FROM (tlibro AS li FULL OUTER JOIN tautor AS au
TEST(# ON (li.autor_id = au.autor_id)
TEST(# FULL OUTER JOIN ttema AS te
TEST(# ON (li.tema_id = te.tema_id));
titulo
|
nombre
| apellidos
|
nombre
---------------------------+----------------+------------+----------Primero sueño
| Sor Juana Inés | de la Cruz | Literatura
Los bandidos de Río Frío
| Manuel
| Payno
| Literatura
Clemencia
| Ignacio Manuel | Altamirano | Literatura
Ficciones
| Jorge Luis
| Borges
| Literatura
Todos los fuegos el fuego | Julio
| Cortázar
| Literatura
Cuentos crueles
| Recuerdo
| Literatura
| No
Artificios
| Jorge Luis
| Borges
| Literatura
El túnel
|
|
|
El llano en llamas
|
|
|
| Anderson
| Imbert
|
(10 rows)
3.3.3. Instrucción CASE
SQL tiene la posibilidad de aplicar expresiones CASE en la salida de las
consultas. Estas expresiones son similares a las de cualquier lenguaje de
programación, la sintaxis es la siguiente:
CASE WHEN condition1 THEN result1
WHEN condition2 THEN result2
[ … ]
[ ELSE default_result ]
END [ AS alias ]
Vamos a suponer que deseamos obtener los libros vendidos con una leyenda que
indique si el precio de venta fue menor, igual o mayor a 100 pesos. La consulta se
resolvería de la siguiente manera.
TEST=# SELECT li.titulo,
TEST-# CASE WHEN ve.precio_venta < 100 THEN 'Vendido menor a 100'
TEST-#
WHEN ve.precio_venta = 100 THEN 'Vendido a 100'
TEST-#
ELSE 'Vendido mayor a 100'
TEST-# END AS "Rango de venta", ve.precio_venta
TEST-# FROM tlibro AS li INNER JOIN tventa AS ve
TEST-# ON (li.libro_id = ve.libro_id);
titulo
|
Rango de venta
| precio_venta
---------------------------+---------------------+-------------Todos los fuegos el fuego | Vendido menor a 100 |
90
Todos los fuegos el fuego | Vendido menor a 100 |
90
Primero sueño
| Vendido a 100
|
100
Primero sueño
| Vendido mayor a 100 |
110
Primero sueño
| Vendido mayor a 100 |
110
Ficciones
| Vendido mayor a 100 |
120
(6 rows)
3.3.4. Subconsultas
También es posible utilizar una consulta (SELECT) como base u origen de otra. A
tal situación se le llamará subconsulta. Por ejemplo, puedes obtener los nombres
de los autores a partir de un SELECT * FROM tautor, en lugar de hacerlo de la
tabla en sí.
TEST=# SELECT nombre FROM (SELECT * FROM tautor) AS autores;
nombre
---------------Ignacio Manuel
Manuel
Jorge Luis
Sor Juana Ines
Julio
No
(6 rows)
Obtengamos los libros con autores cuyo apellido comience con la letra I o L a
partir de una subconsulta.
TEST=# SELECT titulo FROM tlibro
TEST-# WHERE autor_id IN
TEST-# (SELECT autor_id FROM tautor
TEST(# WHERE nombre ~ '^[I-J]');
tíitulo
--------------------------Todos los fuegos el fuego
Ficciones
Artificios
Clemencia
(4 rows)
3.3.5. Consultas de agrupamiento
Es posible obtener con SQL varios registros agrupados en uno sólo.
Adicionalmente podemos contar o sumar los registros del grupo mediante
funciones de agregado.
La idea del agrupamiento es muy importante para generar reportes y consultas
útiles al usuario. Éste no espera que la máquina muestre una lista interminable de
registros sobre los cuales hay que contar y sumar. Nuestros usuarios esperan
datos agrupados por categorías con totales de esas categorías.
Imagina que deseamos saber cuántas ediciones (ISBN) existen por libro.
Podríamos obtener la lista de libros con sus ISBN y ponernos a contar cuántas
veces se repiten estos últimos.
TEST=# SELECT li.titulo, ed.isbn
TEST-# FROM tlibro AS li INNER JOIN tedicion AS ed
TEST-# ON (li.libro_id = ed.libro_id);
titulo
| isbn
---------------------------+-----Cuentos crueles
| 1001
Cuentos crueles
| 1002
Todos los fuegos el fuego | 2001
Primero sueño
| 3001
Primero sueño
| 3002
Ficciones
| 4001
(6 rows)
En lugar de la consulta anterior, construyamos una instrucción con la cláusula
GROUP BY. Esta cláusula permite agrupar los renglones en ciertas categorías y
contar o sumar valores de esas categorías.
TEST=# SELECT li.titulo, COUNT(ed.isbn) AS "Total de ediciones"
TEST-# FROM tlibro AS li INNER JOIN tedicion AS ed
TEST-# ON (li.libro_id = ed.libro_id)
TEST-# GROUP BY li.titulo;
titulo
| Total de ediciones
---------------------------+-------------------Cuentos crueles
|
2
Ficciones
|
1
Primero sueño
|
2
Todos los fuegos el fuego |
1
(4 rows)
La función COUNT (tabla.columna) es un ejemplo de función de agregado.
Permite obtener el conteo de registros del grupo. Si deseamos obtener la suma
total por grupo de alguna columna, debemos usar la función SUM (tabla.columna).
Veamos un ejemplo.
TEST=# select * from tventa;
libro_id | isbn | precio_venta | fecha_venta
----------+------+--------------+------------2 | 2001 |
90 | 2004-05-12
2 | 2001 |
90 | 2004-05-12
3 | 3001 |
100 | 2004-05-12
3 | 3002 |
110 | 2004-05-12
3 | 3002 |
110 | 2004-05-12
4 | 4001 |
(6 rows)
120 | 2004-05-12
TEST=# SELECT li.titulo, SUM(ve.precio_venta) AS "Total_venta"
TEST-# FROM tlibro AS li INNER JOIN tventa AS ve
TEST-# ON (li.libro_id = ve.libro_id)
TEST-# GROUP BY li.titulo;
titulo
| Total_venta
---------------------------+------------Ficciones
|
120
Primero sueño
|
320
Todos los fuegos el fuego |
180
(3 rows)
Si quisiéramos obtener sólo aquellos grupos con determinada condición utilizamos
la cláusula HAVING. Esta cláusula es el equivalente al WHERE pero actúa a nivel
de grupos y sus criterios usan las funciones de agregado. Determinemos los libros
que tienen más de una edición.
TEST=# SELECT li.titulo, COUNT(ed.isbn) AS "Total de ediciones"
TEST-# FROM tlibro AS li INNER JOIN tedicion AS ed
TEST-# ON (li.libro_id = ed.libro_id)
TEST-# GROUP BY li.titulo
TEST-# HAVING COUNT(ed.isbn) > 1;
titulo
| Total de ediciones
-----------------+-------------------Cuentos crueles |
2
Primero sueño
2
(2 rows)
|
3.3.6. Operadores avanzados
Los operadores avanzados de SQL permiten combinar tablas con la posibilidad de
escribir subconsultas para realizar operaciones de UnionUnión, Intersección y
Diferencia, a continuación se explican estas operaciones:
Unión
Permite obtener el conjunto completo de renglones de dos consultas. No duplica
renglones en la salida. Por ejemplo, el siguiente SELECT obtiene la unión de los
titulo de libros y de autores. Las columnas que se unen en la salida deben ser del
mismo tipo.
TEST=# SELECT titulo FROM tlibro
TEST-# UNION
TEST-# SELECT nombre FROM tautor;
titulo
--------------------------Anderson
Artificios
Clemencia
Cuentos crueles
El llano en llamas
El túnel
Ficciones
Ignacio Manuel
Jorge Luis
Julio
Los bandidos d e Río Frío
Manuel
No
Primero sueño
Sor Juana Inés
Todos los fuegos el fuego
(16 rows)
Intersección
Permite obtener los renglones que se encuentran en las dos consultas, los que
aparecen en ambos. Por ejemplo, el siguiente SELECT determina los libros
vendidos a partir de las tablas ‘tlibro’ y ‘tventa’.
TEST=# SELECT libro_id FROM tlibro
TEST-# INTERSECT
TEST-# SELECT libro_id FROM tventa;
libro_id
---------2
3
4
(3 rows)
Diferencia
Obtiene, a partir de dos consultas, aquellos renglones que están del lado
izquierdo, pero no en el derecho. Por ejemplo, intentemos obtener aquellos libros
que no se han vendido.
TEST=# SELECT libro_id
FROM tlibro
TEST-# EXCEPT
TEST-# SELECT libro_id FROM tventa;
libro_id
---------1
5
6
7
8
9
(6 rows)
3.3.7. Plan de ejecución de consultas
Todos los sistemas manejadores de bases de datos nos permiten analizar cómo
fue realizada nuestra consulta. Los datos que podemos obtener de la ejecución de
una consulta son: las tablas que fueron utilizadas, el método de acceso para
obtener los datos de esas tablas (secuencial, indexado, etc.) y la manera como se
realizó el join entre las tablas. A todos estos datos los llamamos “plan de
ejecución”.
Para obtener el plan de ejecución de una consulta utilizamos el
comando EXPLAIN. Veamos un ejemplo.
EXPLAIN ANALYZE SELECT titulo FROM tlibro WHERE autor_id = 11;
3.3.8. Creación de vistas
Una vista es un objeto de la base de datos que almacena una consulta. Funciona
como una tabla, pero la vista no existe físicamente en la base de datos, se genera
de forma dinámica. Una vista nos permite encapsular consultas que utilizamos de
forma recurrente, nos evita escribirlas de nuevo. También nos ayuda a manipular
consultas muy complejas de una forma más sencilla. Por ejemplo, construyamos
una vista con los libros vendidos de mayor a menor venta.
CREATE VIEW vlibrosvendidos
AS
SELECT li.titulo, COUNT(*) AS totventas
FROM tlibro AS li INNER JOIN tventa AS ve
ON (li.libro_id = ve.libro_id)
GROUP BY li.titulo
ORDER BY COUNT(*) DESC;
Obtengamos ahora el libro más vendido
TEST=# SELECT * FROM vlibrosvendidos
TEST-# LIMIT 1;
titulo
| totventas
---------------+----------Primero sueño |
3
(1 row)
Utiliza las vistas como un mecanismo para optimizar la programación de tus
interfaces y para mostrar sólo aquellos datos que son necesarios de acuerdo al
tipo de usuario que los va a consultar.
3.3.9. Consultas especializadas
Podemos utilizar la cláusula ORDER BY
para pedir a SQL que ordene los
registros de salida. Obtengamos la lista de autores ordenados por apellido.
TEST=# SELECT apellidoS, nombre
TEST-# FROM tautor
TEST-# ORDER BY apellidoS ASC;
apellidos
|
nombre
------------+----------------
Altamirano | Ignacio Manuel
Borges
| Jorge Luis
Cortázar
| Julio
Imbert
| Anderson
Payno
| Manuel
Recuerdo
| No
de la Cruz | Sor Juana Inés
(7 rows)
Ordenemos las ventas por libro, alfabéticamente, y por fecha más reciente.
Observa cómo utilizamos constantes numéricas para referirnos a las columnas de
salida que deseamos ordenar.
TEST=# SELECT li.titulo, ve.fecha_venta
TEST-# FROM tlibro li INNER JOIN tventa ve
TEST-# ON (li.libro_id = ve.libro_id)
TEST-# ORDER BY 1 ASC, 2 DESC;
título
| fecha_venta
---------------------------+------------Ficciones
| 2004-05-12
Primero sueño
| 2004-05-12
Primero sueño
| 2004-05-12
Primero sueño
| 2004-05-12
Todos los fuegos el fuego | 2004-05-12
Todos los fuegos el fuego | 2004-05-12
(6 rows)
Cuando ejecutamos un comando SELECT, obtenemos todos los registros que
cumplen nuestras condiciones. También es posible determinar cuántos registros
queremos en la salida de una consulta. Para ello, utilizamos las cláusulas LIMIT y
OFFSET.
LIMIT permite indicar cuántos registros queremos en la salida. Por ejemplo,
obtenga los cinco primeros libros ordenados por título.
TEST=# SELECT titulo
TEST-# FROM tlibro
TEST-# ORDER BY titulo
TEST-# LIMIT 5;
título
-------------------Artificios
Clemencia
Cuentos crueles
El llano en llamas
El túnel
(5 rows)
Con la cláusula OFFSET podemos indicar cuántos registros debe saltar SQL antes
de darnos la salida.
TEST=# SELECT titulo
TEST-# FROM tlibro
TEST-# ORDER BY titulo
TEST-# LIMIT 5
TEST-# OFFSET 2;
titulo
-------------------------Cuentos crueles
El llano en llamas
El túnel
Ficciones
Los bandidos de Río Frío
(5 rows)
3.3.10. Lenguajes de programación de bases de datos
Los sistemas de bases de datos, por definición teórica, incluyen dos lenguajes.
Por una parte, un lenguaje de definición de datos (DDL, Data Definition
Language), y por otra, un lenguaje de manipulación de datos (DML, Data
Manipulation Language). Con el DDL, podemos crear todos los objetos de
almacenamiento de datos, las restricciones impuestas a los mismos y los permisos
de acceso que éstos tendrán. Con el DML, somos capaces de insertar, modificar o
eliminar datos2. En la práctica, ambos forman un solo lenguaje de programación
conocido como SQL.
Los lenguajes de manipulación de datos pueden ser procedurales (es necesario
especificar qué datos se necesitan y cómo obtenerlos) o declarativos (sólo es
necesario indicar qué datos se necesitan y no cómo obtenerlos). El lenguaje SQL
2
Algunos autores incluyen en el DML las instrucciones para consultar datos, pero otros prefieren hablar de un
lenguaje de consulta de datos (DQL, Data Query Language).
pertenece al segundo grupo, esto que lo hace sencillo. Pero, a pesar de que el
SQL resulta un lenguaje poderoso de manipulación de datos, su carácter no
procedural impiden que lo utilicemos para programar toda la lógica de negocio de
una organización. Es entonces necesario echar mano de otros lenguajes que sí
son procedurales. A partir de la versión 1999 del SQL, podemos programar, dentro
de la base de datos, procedimientos y funciones.
Así, todo DBMS cuenta con un lenguaje de programación, que resulta de la unión
entre un lenguaje procedural y el SQL. Esta unión es necesaria ya que el SQL no
incluye estructuras de control como IF-THEN-ELSE, ni estructuras iterativas como
FOR o WHILE. Por ejemplo, Oracle tiene el lenguaje pl/sql, PostgreSQL cuenta
con pl/pgsql, y SQL Server incluye su Transact-SQL.
3.3.11. Procedimientos almacenados de bases de datos
La construcción de programas en una base de datos cambia de manejador en
manejador. Pero también es posible generalizar a partir de conocer la manera de
programar en un manejador. Los programas que creamos en una base de datos
se llaman genéricamente procedimientos almacenados (stored procedures).
Podemos decir que son programas que combinan instrucciones de un lenguaje
procedural con instrucciones de SQL.
Generalmente, utilizamos los procedimientos almacenados para agrupar varias
operaciones realizadas sobre la base de datos. Una vez realizado el
procedimiento almacenado, ya no ejecutamos las operaciones una por una, sino
que ejecutamos el procedimiento en un solo momento.
También se recomiendan cuando tenemos tareas que deseamos automatizar. La
mayoría de los manejadores de bases de datos permiten programar la ejecución
de un procedimiento en un momento determinado. Así, podemos ejecutar tareas
de administración y mantenimiento de la base de datos de forma automática.
Finalmente,
utilizamos
procedimientos
almacenados
para
encapsular
las
modificaciones a los datos de la base, con el fin de reducir la complejidad de
programación de aplicaciones en la capa de interfaces y mejorar la seguridad de
nuestra aplicación escondiendo la lógica de actualización de datos. En nuestro
caso, revisaremos la manera de construir funciones con pl/pgsql en PostgreSQL.
Una función en pl/pgsql tiene la siguiente estructura:
CREATE FUNTION identifier (arguments) RETURNS type AS ‘
DECLARE
declaration;
[…]
BEGIN
statement;
[…]
END;
‘ LANGUAGE ‘plpgsql’;
Por ejemplo, pensemos en programar una función que sume dos números dentro
de la base de datos. El procedimiento o, en este caso, función queda almacenado
en la base de datos como un objeto. Para ejecutar la función utilizamos la
instrucción SELECT.
CREATE FUNCTION suma() RETURNS integer AS ‘
DECLARE
suma integer;
BEGIN
suma := 10 + 10;
return suma;
END;
‘ LANGUAGE ‘plpgsql’;
TEST=# SELECT suma() AS "Suma";
Suma
-----20
(1 row)
Algunos manejadores como SQL Server, permiten que la salida de un
procedimiento sea una consulta de datos. PostgreSQL, por ejemplo, no lo permite;
para hacerlo es necesario usar cursores. Lo que sí podemos hacer es utilizar la
instrucción SELECT INTO para guardar valores de una o varias columnas en
variables.
SELECT
INTO
targaet_variable
[,
,,,]
target_column
[,
...]
select_clauses;
Veamos un ejemplo.
CREATE FUNCTION get_autor(integer) RETURNS text AS ‘
DECLARE
vautor_id ALIAS FOR $1;
vnombre text;
vapellido text;
BEGIN
SELECT INTO vnombre, vapellido nombre, apellidos FROM tautor
WHERE autor_id = vautor_id;
IF NOT FOUND THEN
RETURN ‘’Autor no existe’’;
ELSE
RETURN vnombre || ‘’ ’’ || vapellido;
END IF;
END;
‘ LANGUAGE ‘plpgsql’;
TEST=# SELECT get_autor(11);
get_autor
------------------Jorge Luis Borges
(1 row)
Los argumentos de la función son asociados a identificadores precedidos del signo
‘$’ en el mismo orden en el que se pasan a la función. La cláusula ALIAS la
utilizamos para distinguir de forma más clara cada argumento asignándole un
sobrenombre.
El comando FOUND toma valor de true si el SELECT INTO inmediato anterior fue
exitoso. Debe ser usado en una sentencia IF/THEN. La estructura de la
condicional IF es la siguiente.
IF condition THEN
statement;
[…]
ELSE
statement;
[…]
END IF;
Hay tres ciclos en PL/pgSQL. El primero es un loop básico.
LOOP
statement;
[...]
EXIT WHEN condition;
END LOOP
Un ejemplo sería el siguiente.
CREATE FUNCTION cuadrado (integer) RETURNS integer AS ‘
DECLARE
num1 ALIAS FOR $1;
result integer;
BEGIN
result := num1;
LOOP
result := result * result;
EXIT WHEN result >= 10000;
END LOOP;
RETURN result;
END;
‘ LANGUAGE ‘plpgsql’;
TEST=# SELECT cuadrado(3);
cuadrado
---------43046721
(1 row)
El segundo es un ciclo WHILE.
WHILE condition LOOP
statement;
[ ... ]
END LOOP;
El siguiente es un ejemplo.
CREATE FUNCTION ciclo_suma(integer, integer) RETURNS integer AS ‘
DECLARE
menor ALIAS FOR $1;
mayor ALIAS FOR $2;
result integer = 0;
BEGIN
WHILE result != mayor LOOP
result := result + 1;
END LOOP;
RETURN result;
END;
‘ LANGUAGE ‘plpgsql’;
TEST=# SELECT ciclo_suma(3, 30);
ciclo_suma
-----------30
(1 row)
El tercero es un ciclo FOR. Podemos usarlo en un intervalo de valores o para
todos los renglones de un SELECT.
FOR identifier IN [ REVERSE ] expression1 .. expresion2 LOOP
statement;
[ … ]
END LOOP;
FOR record_variable IN select_statement LOOP
statements;
[ … ]
END LOOP;
A continuación hay un ejemplo.
CREATE FUNCTION total_ventas() RETURNS integer AS ‘
DECLARE
result integer = 0;
vrenglon tventa%ROWTYPE;
BEGIN
FOR vrenglon IN SELECT * FROM tventas LOOP
result := result + vrenglon.precio_venta;
END LOOP;
RETURN result;
END;
‘ LANGUAGE ‘plpgsql’;
TEST=# SELECT total_ventas();
total_ventas
-------------620
(1 row)
3.3.12. Manejo de transacciones
Una transacción es un conjunto de instrucciones DML que se realizan todas o no
se realiza ninguna. Estas actualizaciones están sincronizadas con la base de
datos, la cual implementa mecanismos para evitar problemas de actualización por
concurrencia. Una transacción puede terminar en una actualización de los datos
de la base (commit) o puede terminar sin actualizar la base, regresándola al
estado consistente con el cuál empezó (rollback).
Una transacción comienza con la palabra BEGIN, todas las instrucciones
siguientes a ella forman parte de la transacción. Para que los cambios sean
permanentes debe terminar con el comando COMMIT, en caso de querer
deshacer los cambios utilizamos ROLLBACK. El siguiente es un ejemplo.
TEST=# BEGIN;
BEGIN
TEST=# INSERT INTO ttema VALUES(1, 'Filosofia');
INSERT 49932 1
TEST=# SELECT * FROM ttema;
tema_id |
nombre
---------+-----------5 | Literatura
1 | Filosofia
(2 rows)
TEST=# ROLLBACK;
ROLLBACK
TEST=# SELECT * FROM ttema;
tema_id |
nombre
---------+-----------5 | Literatura
(1 row)
3.3.13. Cursores
Un cursor es un apuntador a un conjunto de registro producidos por un comando
SELECT.
Es
utilizado
generalmente
por
aplicaciones
conectadas
permanentemente a la base de datos y que necesitan recuperar renglones
constantemente. La ventaja de usar un cursor es que no se necesita reejecutar el
query para pedir los valores más recientes en la base. Adicionalmente, los
resultados del query no se almacenan en la memoria de la aplicación cliente.
Usando cursores
Syntax:
DECLARE cursorname [ BINARY ] [ INSENSITIVE ] [ SCROLL ]
CURSOR FOR query
[ FOR { READ ONLY | UPDATE [ OF column [, ...] ] ]
DECLARE sirve para declarar el cursor y ejecutar la instrucción SELECT. Sólo
podemos declarar cursores dentro de una transacción en PostgreSQL.
Syntax:
FETCH [ FORWARD | BACKWARD | RELATIVE ] [ # | ALL | NEXT | PRIOR ]
{ IN | FROM } cursor
FETCH recupera los renglones que produjo la instrucción SELECT.
Vamos a ver un ejemplo.
TEST=# BEGIN;
BEGIN
TEST=# DECLARE cur_autores CURSOR
TEST-#
FOR SELECT * FROM tautor;
DECLARE CURSOR
TEST=# FETCH 4 FROM cur_autores;
autor_id |
nombre
| apellidos
----------+----------------+-----------4 | Ignacio Manuel | Altamirano
3 | Manuel
| Payno
11 | Jorge Luis
| Borges
6 | Sor Juana Inés | de la Cruz
(4 rows)
TEST=# FETCH NEXT FROM cur_autores;
autor_id | nombre | apellidos
----------+--------+----------7 | Julio
| Cortázar
(1 row)
TEST=# FETCH PRIOR FROM cur_autores;
autor_id |
nombre
| apellidos
----------+----------------+-----------6 | Sor Juana Inés | de la Cruz
(1 row)
TEST=# FETCH BACKWARD 3 FROM cur_autores;
autor_id |
nombre
| apellidos
----------+----------------+-----------11 | Jorge Luis
3 | Manuel
| Borges
| Payno
4 | Ignacio Manuel | Altamirano
(3 rows)
TEST=# FETCH 3 FROM cur_autores;
autor_id |
nombre
| apellidos
----------+----------------+-----------3 | Manuel
11 | Jorge Luis
| Payno
| Borges
6 | Sor Juana Inés | de la Cruz
(3 rows)
TEST=# COMMIT;
COMMIT
También podemos mover la posición del cursor hacia un registro en particular con
la instrucción MOVE.
Syntax:
MOVE [ direction ] [ count ]
{ IN | FROM } cursor
Un ejemplo sería el siguiente.
TEST=# BEGIN;
BEGIN
TEST=# DECLARE cur_autores CURSOR
TEST-#
FOR SELECT * FROM tautor;
DECLARE CURSOR
TEST=# FETCH FROM cur_autores;
autor_id |
nombre
| apellidos
----------+----------------+-----------4 | Ignacio Manuel | Altamirano
(1 row)
TEST=# MOVE FORWARD 4
TEST-#
IN cur_autores;
MOVE 4
TEST=# FETCH FROM cur_autores;
autor_id |
nombre
| apellidos
----------+----------+----------1 | Anderson | Imbert
(1 row)
TEST=# FETCH FROM cur_autores;
autor_id | nombre | apellidos
----------+----------+----------2 | Villiers | de L'isle
(1 row)
TEST=# COMMIT;
COMMIT
Cerrando cursores
Para cerrar un cursor podemos hacerlo con la instrucción CLOSE.
Syntax:
CLOSE cursor
Adicionalmente, si cerramos el bloque de la transacción con COMMIT O
ROLLBACK el cursor se cierra automáticamente.
3.3.14. Características orientadas a objetos
El estándar SQL de 1999, conocido como SQL3, incorporó a este lenguaje
algunas características de la orientación a objetos. Una de las más importantes es
la herencia de tablas para lo cuál cada tabla debe estar identificada con un
identificador de objeto. Los mismo sucede con los renglones, éstos están
identificados por un ID.
Columnas de sistema
Todas las tablas en PostgreSQL incluyen, además de las columnas definidas por
el usuario, algunas columnas con información que el propio sistema actualiza. Dos
de ellas son la columna oid (object identifier) y tableoid (table object identifier).
La primera es un número único que identifica a cada renglón en la tabla. La
segunda es un identificador único en el servidor para cada tabla. A continuación
puedes ver cómo obtener estas columnas.
TEST=# select oid from pruebafechas;
oid
------41481
41482
41483
41484
(4 rows)
TEST=# select tableoid from pruebafechas;
tableoid
---------41479
41479
41479
41479
(4 rows)
Herencia de tablas
Es un mecanismo objeto-relacional que permite a una tabla heredar atributos de
una o más tablas. Esta situación crea una relación de padres e hijas entre las
tablas. La tabla hija tendría sus propias columnas y adicionalmente las de su tabla
padre (sólo se permite un padre).
Una consulta sobre la tabla padre puede realizarse sólo para sus columnas o
incluyendo las columnas de sus descendientes. La tabla hija nunca regresa las
columnas de sus padres. Veamos un ejemplo del uso de la herencia de tablas.
Creemos una tabla padre y agreguemos un registro.
CREATE TABLE trabajador
(
nombre varchar(25) NOT NULL,
apellidop varchar(25) NOT NULL,
trabajador_id integer PRIMARY KEY
);
TEST=# INSERT INTO trabajador VALUES('Carlos', 'Mendez', 1);
INSERT 41545 1
TEST=# SELECT * FROM trabajador;
nombre | apellidop | trabajador_id
--------+-----------+--------------Carlos | Méndez
|
1
Vamos a crear ahora una tabla hija. Observa el uso de INHERITS para referenciar
la tabla padre.
CREATE TABLE trabhon
(
retiva numeric,
retisr numeric
)
INHERITS (trabajador);
Ahora, insertemos un registro en la tabla hija.
TEST=# INSERT INTO trabhon VALUES('Arturo', 'Garcia', 2, 10,10);
INSERT 41551 1
Las siguientes consultas muestran la información de las tablas.
TEST=# SELECT * FROM trabhon;
nombre | apellidop | trabajador_id | retiva | retisr
--------+-----------+---------------+--------+-------Arturo | Garcia
|
2 |
10 |
(1 row)
TEST=# SELECT * FROM trabajador;
nombre | apellidop | trabajador_id
--------+-----------+--------------Carlos | Mendez
|
1
Arturo | Garcia
|
2
(2 rows)
TEST=# SELECT * FROM ONLY trabajador;
nombre | apellidop | trabajador_id
--------+-----------+--------------Carlos | Méndez
(1 row)
|
1
10
Descargar