|      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ónDurante 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ísTodas 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 :
 
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. La capa de acceso a los datos La capa gestora SQL La capa traductora SQL La capa de comunicaciones Primer PasoVamos 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.FuncionesA 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 ConceptosHemos 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:
 
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:
| 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. |  
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 : 
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.
| 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 |  
 |  
| DELETE | Con este borraremos tuplas de una tabla |  
| UPDATE | Con este modificaremos columnas de tuplas de una tabla |  ResumenAunque 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.
           |