SQL: Introducción al SQL. Instalación de PostgreSQL

por Manuel Soriano


    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).

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 http://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 :

  1. La capa de acceso a los datos
  2. La capa gestora SQL
  3. La capa traductora SQL
  4. 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.


    © 1998 Manuel Soriano
    Permission is granted to copy, distribute and/or modify this page under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts and no Back-Cover Texts.