Este cursillo de SQL se hará
en tres parte, en la primera nos centraremos en la generalidad del
SQL, trataremos con postgreSQL que es un servidor de bases de datos
vía SQL de dominio público. La segunda parte estudiaremos los comandos
SQL con un mayor detalle. En la tercera parte trataremos comando SQL
con opciones avanzadas así como funciones del propio gestor PostgreSQL
que pueden ser interesantes para nuestros desarrollos y haremos un
pequeño programa en C.
Introducción
Durante esta rápida introducción sólo hablaré de gestores de base de
datos. Existen otras organizaciones de datos, pero hablar de ellas se
saldría del objetivo del curso.
Hasta el momento, el acceso a los datos se hacía mediante accesos a
entidades que se relacionaban entre sí mediante una ligaduras
definidas en el esquema de la base de datos, eso tenía una ventaja,
rapidez, pero una gran desventaja, sólo podíamos acceder a los datos
mediante una ligadura, ejemplo :
país -> provincias -> municipios
pero nunca :
país -> municipios
Siendo "->" la ligadura.
Si queríamos realizar esa segunda relación, debíamos redefinir el esquema y
recompilarlo...
En efecto, en una BD jerarquica, la relación entre las diversas entidades es estática y solo modificable mediante modificación del esquema de la base de datos y recompilacion de este ultimo.
La idea básica de los gestores de bases de datos relacionales es
justamente ligar los datos en el momento de la petición de estos, pero
sin necesitar una ligadura estática, sino una identificación que
permita ligar un registro con otro.
Esto que acabo de escribir necesita una Aspirina :-)
Los gestores de base de datos relacionales no precisan unas ligaduras
estáticas para poder descender una jerarquia de entidades, sino que usan un
código único que les identifica para realizar una relación temporanea que es el
resultado de una pregunta al gestor.
Esta identificación no es más que el código. Ej: mi número de telefono
no es el :
1234567
sino el :
34 6 1234567
En efecto mi numero de telefono esta identificado por el código país (34),
el código de la provincia (6) y el propio número de aparato (1234567).
- En la entidad paises, el código 34 (España) es único.
- En la entidad provincias, el código 34-6 (España/Valencia) es único.
- En la entidad aparatos, el código 34-6-1234567 (España/Valencia/mi telefono) es único.
Vamos a poner las bases del primer ejemplo que ilustrara lo que acabo de decir.
Todos los municipios tienen un código, pertenecen a una provincia y a un país
Todas las provincias tienen un código y pertenecen a un país
Todos los países tienen un código
Para conocer todos los municipios de una provincia, relaciono el
municipio con la provincia por el código de país y provincia; para
saber todos los municipio de un país, relaciono el municipio con el
país por el código de país. Estas relaciones son temporáneas y sólo
existen durante la realización de mi pregunta.
Es un poco duro, pero con los primeros ejemplos comprenderemos un poco mejor
este concepto de código y de pertenencia.
Al realizar mi pregunta el gestor me entregara todos los datos que se
relacionen entre sí. Pero ¿qué datos me va a dar? Pues la conjunción
de los datos de países y municipios, para cada municipio me repetirá
los datos del país.
Durante la realización de mi pregunta se ha creado un nueva entidad
que no tiene nombre y que contiene una réplica de países y
municipios. Esa nueva entidad, y me repito, desaparecerá una vez
terminada mi lectura.
Antes llamábamos a los conjuntos de datos, ficheros. Estos se componen
de registros y estos últimos se componen de campos. Bien, pues en una
base de datos relacional, un "fichero" se llama tabla, una tabla se
compone de tuplas y una tupla contiene columnas, no es más que un
matiz... ;-)
Hay que destacar que ciertos gestores de BD jerárquicos introducían
SQL como lenguaje de acceso, pero esto es anecdótico. El lenguaje SQL
es casi una exclusividad de los gestores relacionales.
Para ilustrar el curso utilizaremos el gestor relacional PostgreSQL,
aunque no cumple con todas las normas SQL, sí que es más que
suficiente para nosotros, y para otros menesteres más duros también.
Voy a explicar muy brevemente el proceso de instalación, dado que el
objetivo de este artículo es SQL.
Primero bajamos los fuentes de www.postgresql.org, así como los
parches. Los extraemos (tar zxvf) en un directorio, cd
postgresql-6.3
cd src
./configure --prefix=/el/path/deseado
make all >& make.log &
tail -f make.log
export PATH=$PATH:/el/path/deseado/pgsql/bin
export MANPATH=$MANPATH:/el/path/deseado/pgsql/man
export PGLIB=/el/path/deseado/pgsql/lib
export PGDATA=/el/path/deseado/pgsql/data
initdb
createdb prueba
psql prueba
Welcome to the POSTGRESQL interactive sql monitor:
Please read the file COPYRIGHT for copyright terms of POSTGRESQL
type \? for help on slash commands
type \q to quit
type \g or terminate with semicolon to execute query
You are currently connected to the database: postgres
prueba=>
Este es el prompt de postgres, ahora podemos ejecutar comandos.
prueba=>create table prueba (campo1 varchar(10));
CREATE
prueba=>insert into prueba values ('hello');
INSERT numerito 1
prueba=>commit work;
NOTICE:EndTransactionBlock and not inprogress/abort state
END
prueba=>select * from prueba;
campo1
------
hello
(1 row)
prueba=>drop table prueba;
DROP
prueba=>Ctrl-d
Ya estamos fuera del monitor SQL.
Si no habéis conseguido compilar e instalar Postgres95 correctamente,
referiros al fichero INSTALL que está en el directorio de entrada de
la distribución.
Como comentario, vamos a ver como esta construido un servidor de bases de
datos relacional :
- La capa de acceso a los datos
- La capa gestora SQL
- La capa traductora SQL
- La capa de comunicaciones
Como cliente nos conectaremos a la capa 4, le enviaremos los comandos
SQL a esta capa, que los pasará a la capa 3. Ésta hace la traducción
del comando y, si no hay errores, envía el comando a la capa 2. La
capa 2 hace toda la gestión del comando con la colaboración de la capa
1: recoge los datos y errores para enviarlos al cliente, vía la capa
4; y es capaz de mantener un diálogo con el programa cliente para
coordinarse. La capa 1 es la encargada de gestionar correctamente los
datos y controlar los bloqueos y transacciones.
Primer Paso
Vamos a ilustrar con datos lo que antes he explicado, así que vamos a
crear 3 tablas (o ficheros) :
Fichero: paises.sql
create table paises (cod_pais integer, nombre varchar(30));
insert into paises values (1, 'pais 1');
insert into paises values (2, 'pais 2');
insert into paises values (3, 'pais 3');
commit work;
Fichero: provincias.sql
create table provincias (cod_provincia int,
cod_pais int,
nom_provincia varchar(30));
insert into provincias values (1, 1, 'Provincia 1, Pais 1');
insert into provincias values (2, 1, 'Provincia 2, Pais 1');
insert into provincias values (1, 2, 'Provincia 1, Pais 2');
insert into provincias values (2, 2, 'Provincia 2, Pais 2');
insert into provincias values (1, 3, 'Provincia 1, Pais 3');
insert into provincias values (2, 3, 'Provincia 2, Pais 3');
commit work;
Fichero: municipios.sql
create table municipios (cod_pais int,
cod_provincia int,
cod_municipio int,
nombre_municipio varchar(60));
insert into municipios values (1, 1, 1, 'Municipio 1, Provincia 1, Pais 1');
insert into municipios values (2, 1, 1, 'Municipio 2, Provincia 1, Pais 1');
insert into municipios values (3, 1, 1, 'Municipio 3, Provincia 1, Pais 1');
insert into municipios values (1, 2, 1, 'Municipio 1, Provincia 2, Pais 1');
insert into municipios values (2, 2, 1, 'Municipio 2, Provincia 2, Pais 1');
insert into municipios values (3, 2, 1, 'Municipio 3, Provincia 2, Pais 1');
insert into municipios values (1, 3, 1, 'Municipio 1, Provincia 3, Pais 1');
insert into municipios values (2, 3, 1, 'Municipio 2, Provincia 3, Pais 1');
insert into municipios values (3, 3, 1, 'Municipio 3, Provincia 3, Pais 1');
insert into municipios values (1, 1, 2, 'Municipio 1, Provincia 1, Pais 2');
insert into municipios values (2, 1, 2, 'Municipio 2, Provincia 1, Pais 2');
insert into municipios values (3, 1, 2, 'Municipio 3, Provincia 1, Pais 2');
insert into municipios values (1, 2, 2, 'Municipio 1, Provincia 2, Pais 2');
insert into municipios values (2, 2, 2, 'Municipio 2, Provincia 2, Pais 2');
insert into municipios values (3, 2, 2, 'Municipio 3, Provincia 2, Pais 2');
insert into municipios values (1, 3, 2, 'Municipio 1, Provincia 3, Pais 2');
insert into municipios values (2, 3, 2, 'Municipio 2, Provincia 3, Pais 2');
insert into municipios values (3, 3, 2, 'Municipio 3, Provincia 3, Pais 2');
insert into municipios values (1, 1, 3, 'Municipio 1, Provincia 1, Pais 3');
insert into municipios values (2, 1, 3, 'Municipio 2, Provincia 1, Pais 3');
insert into municipios values (3, 1, 3, 'Municipio 3, Provincia 1, Pais 3');
insert into municipios values (1, 2, 3, 'Municipio 1, Provincia 2, Pais 3');
insert into municipios values (2, 2, 3, 'Municipio 2, Provincia 2, Pais 3');
insert into municipios values (3, 2, 3, 'Municipio 3, Provincia 2, Pais 3');
insert into municipios values (1, 3, 3, 'Municipio 1, Provincia 3, Pais 3');
insert into municipios values (2, 3, 3, 'Municipio 2, Provincia 3, Pais 3');
insert into municipios values (3, 3, 3, 'Municipio 3, Provincia 3, Pais 3');
commit work;
Para ejecutar dentro del psql unos comandos sql de un fichero, hacer :
\i nombre_del_fichero
Tambén podemos hacer un cortar y pegar desde estas páginas.
Vamos a ver qué municipios tenemos :
select * from municipios;
cod_pais|cod_provincia|cod_municipio|nombre_municpio
--------+-------------+-------------+--------------------------------
1| 1| 1|Municipio 1, Provincia 1, Pais 1
2| 1| 1|Municipio 2, Provincia 1, Pais 1
3| 1| 1|Municipio 3, Provincia 1, Pais 1
1| 2| 1|Municipio 1, Provincia 2, Pais 1
2| 2| 1|Municipio 2, Provincia 2, Pais 1
3| 2| 1|Municipio 3, Provincia 2, Pais 1
1| 3| 1|Municipio 1, Provincia 3, Pais 1
2| 3| 1|Municipio 2, Provincia 3, Pais 1
3| 3| 1|Municipio 3, Provincia 3, Pais 1
1| 1| 2|Municipio 1, Provincia 1, Pais 2
2| 1| 2|Municipio 2, Provincia 1, Pais 2
3| 1| 2|Municipio 3, Provincia 1, Pais 2
1| 2| 2|Municipio 1, Provincia 2, Pais 2
2| 2| 2|Municipio 2, Provincia 2, Pais 2
3| 2| 2|Municipio 3, Provincia 2, Pais 2
1| 3| 2|Municipio 1, Provincia 3, Pais 2
2| 3| 2|Municipio 2, Provincia 3, Pais 2
3| 3| 2|Municipio 3, Provincia 3, Pais 2
1| 1| 3|Municipio 1, Provincia 1, Pais 3
2| 1| 3|Municipio 2, Provincia 1, Pais 3
3| 1| 3|Municipio 3, Provincia 1, Pais 3
1| 2| 3|Municipio 1, Provincia 2, Pais 3
2| 2| 3|Municipio 2, Provincia 2, Pais 3
3| 2| 3|Municipio 3, Provincia 2, Pais 3
1| 3| 3|Municipio 1, Provincia 3, Pais 3
2| 3| 3|Municipio 2, Provincia 3, Pais 3
3| 3| 3|Municipio 3, Provincia 3, Pais 3
(27 rows)
prueba=>
Bien tenemos 27 tuplas y el pgsql está esperando otro comando,
vamos a ver este :
select * from paises, municipios
cod_pais|nombre|cod_provincia|cod_pais|nom_provincia
--------+------+-------------+--------+-------------------
1|pais 1| 1| 1|Provincia 1, Pais 1
2|pais 2| 1| 1|Provincia 1, Pais 1
3|pais 3| 1| 1|Provincia 1, Pais 1
1|pais 1| 2| 1|Provincia 2, Pais 1
2|pais 2| 2| 1|Provincia 2, Pais 1
3|pais 3| 2| 1|Provincia 2, Pais 1
1|pais 1| 1| 2|Provincia 1, Pais 2
2|pais 2| 1| 2|Provincia 1, Pais 2
3|pais 3| 1| 2|Provincia 1, Pais 2
1|pais 1| 2| 2|Provincia 2, Pais 2
2|pais 2| 2| 2|Provincia 2, Pais 2
3|pais 3| 2| 2|Provincia 2, Pais 2
1|pais 1| 1| 3|Provincia 1, Pais 3
2|pais 2| 1| 3|Provincia 1, Pais 3
3|pais 3| 1| 3|Provincia 1, Pais 3
1|pais 1| 2| 3|Provincia 2, Pais 3
2|pais 2| 2| 3|Provincia 2, Pais 3
3|pais 3| 2| 3|Provincia 2, Pais 3
(18 rows)
¿¿¿18 tuplas ??? Vamos a ver, hemos insertado 3 países, y 6
provincias, todas ellas identificadas en un país. ¿Cómo es posible
que nos saque 18 tuplas?
En este último comando hemos, realizado una unión de dos tablas, hemos
relacionado la tabla de países con la de municipios, como no le hemos
dado ninguna regla de unión, nos ha devuelto TODAS las tuplas de países
relacionadas con TODAS las tuplas de provincias, es decir 3 tuplas de
países por 6 de provincias total 18 tuplas, este resultado es
totalmente ilógico e inútil, ahora mejor hacer:
select * from paises, provincias
where paises.cod_pais = provincias.cod_pais;
cod_pais|nombre|cod_provincia|cod_pais|nom_provincia
--------+------+-------------+--------+-------------------
1|pais 1| 1| 1|Provincia 1, Pais 1
1|pais 1| 2| 1|Provincia 2, Pais 1
2|pais 2| 1| 2|Provincia 1, Pais 2
2|pais 2| 2| 2|Provincia 2, Pais 2
3|pais 3| 1| 3|Provincia 1, Pais 3
3|pais 3| 2| 3|Provincia 2, Pais 3
(6 rows)
Bueno, esto ya empieza a ser más razonable. ¿Seis tuplas, correcto?
Si tenemos seis municipios y cada municipio está en un país. Es normal
que me dé un número de tuplas igual al de municipios, dado que países
es un calificativo de municipios. Acabamos de relacionar la tabla de
países con la tabla de provincias mediante el código de país.
Recordemos que países tiene código y que provincias tiene el código
país al que pertenece.
¿Porqué paises.cod_pais = provincias.cod_pais ?
Código de país en la tabla de países se llama cod_pais y en
la tabla de provincias también, entonces:
cod_pais = cod_pais
es ilógico, el interprete nunca sabría como manejar eso y nos daría un
error:
select * from paises, provincias
where cod_pais = cod_pais;
ERROR: Column cod_pais is ambiguous
Esto ahora podemos dar alias a las columnas :
select * from paises a, provincias b
where a.cod_pais = b.cod_pais;
cod_pais|nombre|cod_provincia|cod_pais|nom_provincia
--------+------+-------------+--------+-------------------
1|pais 1| 1| 1|Provincia 1, Pais 1
1|pais 1| 2| 1|Provincia 2, Pais 1
2|pais 2| 1| 2|Provincia 1, Pais 2
2|pais 2| 2| 2|Provincia 2, Pais 2
3|pais 3| 1| 3|Provincia 1, Pais 3
3|pais 3| 2| 3|Provincia 2, Pais 3
(6 rows)
¿Qué nos devuelve el gestor?: cod_pais, nombre,
cod_provincia, cod_pais y nom_provincia.
Como hemos pedido "select * from países, provincias", el
* es un comodín que indica que queremos TODO, por eso nos
devuelve las dos columnas de países y las 3 de provincias, ahora
queremos algo más explícito
select a.cod_pais, cod_provincia, nombre, nom_provincia
from paises a, provincias b
where a.cod_pais = b.cod_pais;
cod_pais|cod_provincia|nombre|nom_provincia
--------+-------------+------+-------------------
1| 1|pais 1|Provincia 1, Pais 1
1| 2|pais 1|Provincia 2, Pais 1
2| 1|pais 2|Provincia 1, Pais 2
2| 2|pais 2|Provincia 2, Pais 2
3| 1|pais 3|Provincia 1, Pais 3
3| 2|pais 3|Provincia 2, Pais 3
(6 rows)
En este comando hemos especificado que queremos, concretamente el
código del país, el código de la provincia, el nombre del país y el
nombre de la provincia. Fijaros que ciertos nombres de columna están
cualificados (a.cod_pais) mientras que otros no (nom_provincia), esto
es debido a que cod_pais está repetido en ambas tablas mientras que
nom_provincia sólo se encuentra en provincias. Los nombres de columnas
únicos no hace falta cualificarlos.
Complicamos un poco más:
select a.cod_pais, cod_provincia, nombre, nom_provincia
from paises a, provincias b
where a.cod_pais = b.cod_pais
and a.cod_pais = 3;
cod_pais|cod_provincia|nombre|nom_provincia
--------+-------------+------+-------------------
3| 1|pais 3|Provincia 1, Pais 3
3| 2|pais 3|Provincia 2, Pais 3
(2 rows)
Esta vez hemos limitado la busqueda a los que sólo tienen como código
país el 3.
Funciones
A tÍtulo de ejemplo vamos a ver la función de recuento de tuplas,
count().
select count(*) from municipios;
count
-----
27
(1 row)
Nos devuelve el número de tuplas que tiene la tabla de municipios,
ahora:
select cod_pais, count(*) from municipios
group by cod_pais;
cod_pais|count
--------+-----
1| 9
2| 9
3| 9
(3 rows)
Nos devuelve el número de tuplas que tienen el MISMO código de país,
por eso usamos el discriminante cod_pais.
Para ilustrarlo mejor:
select nombre, count(*) from paises a, municipios b
where a.cod_pais = b.cod_pais
group by nombre;
nombre|count
------+-----
pais 1| 9
pais 2| 9
pais 3| 9
(3 rows)
Continuamos teniendo nuestras tres tuplas pero un pelín más claras.
Bueno esto era una mera introducción, una forma de ponernos en calor :-)
Revisión de Conceptos
Hemos visto unos conceptos muy básicos de SQL. Lo más importante es el
concepto básico de SQL. Con éste se deja de trabajar sobre datos
concretos para hacerlo con entidades de datos. Una entidad de datos es
el concepto abstracto de la base de datos. En resumen "DE TODO LO QUE
TIENES SÓLO QUIERO UNA PARTE".
Hemos visto varios comandos:
CREATE TABLE | Este comando permite crear una
tabla con sus columnas. |
DROP TABLE | Borrará la tabla. |
SELECT |
Este comando es la base de SQL, es el que permite crear una tabla
temporal con los datos que necesitamos. Este comando puede contener
funciones o predicados complejos, así como sub_selects:
select count(*) from municipios
where cod_pais in (select cod_pais from paises);
count
-----
27
(1 row)
|
COMMIT WORK | Este es otro comando muy
importante. Este comando indica al gestor que TODAS las modificaciones
desde el BEGIN WORK pueden hacerse definitivas. En nuestro gestor lo
que marca el inicio de una transacción es el BEGIN WORK, en otros
gestores el inicio de una transacción la marca el primer comando que
modifica algo en la base de datos. En postgreSQL todo comando que
modifique datos lo hará directamente, si previamente no se ha
realizado un BEGIN WORK.
NOTA: los comandos que modifican el esquema de la base de datos
realizan un COMMIT WORK, como tal si se ha abierto una transacción y
se realiza un comando que modifique el esquema, nuestra transacción se
vera cerrada, con la imposibilidad de realizar un ROLLBACK WORK.
Mientras un usuario tenga una transacción activa, este podrá decidir
como los demás usuarios tendrán acceso a sus datos :
Datos modificados
Datos originales a la transacción
Bloqueo de acceso
|
COMMIT WORK | Cierra una transacción dejando
las modificaciones como definitivas mientras que ROLLBACK WORK
devuelve los datos al estado anterior al inicio de la
transacción. |
El concepto de transacción es muy importante, dado que permite la
vuelta al estado anterior en caso de haberse producido un error.
Ahora probemos ese concepto, empezamos por un "rollback work"
para cerrar cualquier transacción abierta:
select * from paises;
cod_pais|nombre
--------+------
1|pais 1
2|pais 2
3|pais 3
(3 rows)
Tenemos tres tuplas.
begin work;
Inicio la transacción
insert into paises values (5, 'pais de mentira');
Inserto una tupla.
select * from paises;
cod_pais|nombre
--------+---------------
1|pais 1
2|pais 2
3|pais 3
5|pais de mentira
(4 rows)
Verificamos que están todas y lo están.
rollback work;
Abandonamos la transacción.
select * from paises;
cod_pais|nombre
--------+------
1|pais 1
2|pais 2
3|pais 3
(3 rows)
Todo está como antes.
INSERT |
También lo hemos visto, este comando sirve para meter datos en una
tabla. |
CREATE TABLE | Otro comando muy importante, el
de creación de la tabla y sus columnas, vamos a ver que tipos de datos
podemos tratar :
char(rango): |
Dato alfanumérico de longitud fija de 30 bytes.
|
varchar(rango): |
Dato alfanumérico de longitud variable de hasta 30 bytes. |
int2: | Dato
numérico binario de 2 bytes : 2**-15 hasta 2**15
|
int4: | Dato
numérico binario de 4 bytes : 2**-31 - 2**31
|
money: |
Dato numérico de coma fija, ej: money(6,3), dato numérico de seis
dígitos de los cuales 3 son decimales (3 enteros y tres decimales).
|
time: | Dato
de tiempo que contendrá horas, minutos, segundos, centésimas,
HH:MM:SS:CCC
|
date: | Dato
de fecha que contendrá año, mes, día, AAAA/MM/DD
|
timestamp: |
Dato fecha y hora, AAAA/MM/DD:HH:MM:SS:CCC
|
float(n): |
Dato real de precisión
|
float3: |
Dato real de doble precisión
|
Las definiciones de los tipos de datos son propias a cada gestor,
existe una normalización de SQL (la última es la ANSI/92 o SQL/3) que
define unos tipos de datos con sus características, como tal en este
cursillo sólo veremos unos cuantos propios a PostgreSQL.
|
DELETE | Con este borraremos tuplas de una tabla
|
UPDATE | Con este modificaremos columnas de tuplas de una tabla
|
Resumen
Aunque parezca un tanto revuelto, nos hemos introducido al SQL y
tenemos instalado un gestor de bases de datos relacionales.
SQL nos permite construir una capa de abstracción a los datos y
manejar estos según lo necesitemos.
De la forma que hemos visto esto, cabe una pregunta :
¿Cómo aplico SQL a una aplicación?
La respuesta vendrá poco a poco y en la tercera entrega haremos una
pequeña aplicación en C.
|