Está en la página 1de 170

Diseño de

Aplicaciones
WEB
con
PHP 4
y
ProgreSQL 7.1

Jorge Domínguez Chávez


Título de esta obra

Aplicaciones WEB con PHP 4 y PosgreSQL 7.1

La presentación y disposición en conjunto de esta


obra son propiedad del editor. No está permitida la
reproducción total o parcial de este libro, ni su
tratamiento informático, ni la trasmisión de ninguna
forma o por cualquier medio, ya sea electrónico,
mecánico, por fotocopiadora, la grabación, por
registros o por cualquier otro métodos de
recuperación y almacenamiento de información sin el
permiso previo y por escrito de los titulares del
copyright.
A lo largo del presente libro, se hace mención de
marcas registradas por empresas fabricantes de
hardware y software. Más que poner un símbolo de
“marca registrada” [trademark], cada vez que se hace
mención de ella, se quiere dejar constancia de que se
están utilizando los nombres en el contexto editorial
y en beneficio del propietario de dicha marca
registrada, sin intención de violar o usurpar sus
derechos adquiridos.

Copyright @ 2003 por IEASS y Jorge Domínguez Chávez


Derechos reservados.
Editor: IEASS, Editores
Venezuela, 2003
1234567890

ISBN 980-6366-09-4
Teléfono 0414-5897085
Página WEB www.ieass.tk
Hecho en Venezuela
Tabla de Contenido
Acerca de este libro ................................................................................................................ 8
Presentación de la Obra .......................................................................................................... 9
Convenciones ....................................................................................................................... 10
Introducción a las Aplicaciones WEB.................................................................................. 11
Primera parte ........................................................................................................................ 15
Requerimientos..................................................................................................................... 15
BISON .............................................................................................................................. 16
FLEX ................................................................................................................................ 16
Instalación de Apache/ Php .............................................................................................. 17
Actualización del archivo de configuración ..................................................................... 18
Arranque del servidor ....................................................................................................... 19
Instalación de PostgreSQL ............................................................................................... 20
Cuenta de administración de la BD .................................................................................. 20
Creación de directorios para la correcta instalación de PostgreSQL................................ 20
Proceso de compilación/instalación ................................................................................. 21
Actualización del archivo de configuración ..................................................................... 21
Actualización del archivo profile ..................................................................................... 22
Inicialización y arranque de la BD ................................................................................... 23
Dando privilegios a la BD ................................................................................................ 24
Instalación de PHP ........................................................................................................... 25
Segunda Parte ....................................................................................................................... 27
PostgreSQL........................................................................................................................... 27
El proyecto Postgres de Berkeley..................................................................................... 27
Postgres95......................................................................................................................... 28
PostgreSQL....................................................................................................................... 29
Concurrencia de acceso para multiversión ....................................................................... 30
Herencia, estructura orientada a objetos........................................................................... 32
Procedimientos almacenados............................................................................................ 33
Triggers, constricciones.................................................................................................... 34
Introducción, tablas, filas y columnas .............................................................................. 35
Un poco de vocabulario................................................................................................ 35
Las tablas ...................................................................................................................... 36
NULL, un valor a parte................................................................................................. 37
CREATE TABLE, SELECT, INSERT, DELETE ........................................................... 38
Creación de tablas......................................................................................................... 38
Adición y consulta de datos.......................................................................................... 39
Operaciones con cursores ............................................................................................. 40
Haciendo limpieza ........................................................................................................ 41
WHERE y UPDATE ........................................................................................................ 42
La cláusula WHERE..................................................................................................... 42
UPDATE ...................................................................................................................... 44
DISTINCT e INTO....................................................................................................... 45
Índices, claves y join ........................................................................................................ 46
Índices........................................................................................................................... 46
Join ............................................................................................................................... 48
Los agregados ................................................................................................................... 51
Agrupando los agregados ............................................................................................. 53
Las transacciones.............................................................................................................. 54
Constricciones y secuencias ............................................................................................. 57
SECUENCIAS ............................................................................................................. 59
Constriñendo las tablas................................................................................................. 64
Procedimientos almacenados (funciones) ........................................................................ 66
Triggers (disparadores)..................................................................................................... 74
¿Qué es un trigger? ....................................................................................................... 74
¿Cómo hacer un trigger ? ............................................................................................. 75
Optimizaciones y desempeño ........................................................................................... 80
Niveles de optimización ............................................................................................... 80
Nivel conceptual ........................................................................................................... 80
Escribiendo requerimientoss, tablas directrices y estadísticas ..................................... 82
Requerimientos............................................................................................................. 87
La configuración del servidor....................................................................................... 88
Los usuarios y sus derechos: CREATE GROUP, GRANT. ............................................ 88
GRANT y REVOKE .................................................................................................... 91
Los grupos .................................................................................................................... 93
¿Una Suite...?................................................................................................................ 95
Conclusión........................................................................................................................ 95
Scripts de creación de la base de datos......................................................................... 95
Utilidad de los alias de tablas ....................................................................................... 97
No presuma del resultado de una operación que tenga un NULL................................ 98
Nombres sistemáticos de columnas.............................................................................. 99
Trabajemos siempre en forma modular ........................................................................ 99
Piense en el producto cartesiano de una tabla sobre ella misma .................................. 99
Tercera parte ....................................................................................................................... 103
Historia de PHP .................................................................................................................. 103
Introducción al PHP ........................................................................................................... 103
¿Qué es PHP? ..................................................................................................................... 105
¿Qué hacemos con PHP?................................................................................................ 105
Ventajas de PHP ............................................................................................................. 107
El intérprete .................................................................................................................... 107
Configuración de PHP ........................................................................................................ 108
Archivos de configuración ............................................................................................. 108
Directivas de configuración............................................................................................ 108
Más directivas................................................................................................................. 110
Primeras nociones de sintaxis............................................................................................. 111
Escape............................................................................................................................. 111
Separación de instrucciones............................................................................................ 111
Comentarios.................................................................................................................... 111
Tipos ................................................................................................................................... 113
Enteros ............................................................................................................................ 113
Reales ............................................................................................................................. 113
Cadenas........................................................................................................................... 114
Conversión de cadenas ................................................................................................... 115
Vectores .......................................................................................................................... 115
Objetos............................................................................................................................ 117
Variables............................................................................................................................. 120
Referencia a variables..................................................................................................... 120
Asignación de valores..................................................................................................... 120
Ámbito de las variables .................................................................................................. 121
Variables globales .......................................................................................................... 122
Variables estáticas .......................................................................................................... 123
Variables variables ......................................................................................................... 123
Variables predefinidas .................................................................................................... 124
Constantes....................................................................................................................... 125
Operadores.......................................................................................................................... 126
Operadores aritméticos ............................................................................... 126
Operadores lógicos ......................................................................................................... 126
Operadores de comparación ........................................................................................... 127
Operadores de cadenas ................................................................................................... 128
Operadores a nivel de bits .............................................................................................. 128
Operadores de asignación............................................................................................... 129
Operadores de incremento y decremento (++, --) .......................................................... 129
Operador de control de errores ....................................................................................... 130
Operador de ejecución .................................................................................................... 130
Estructuras de control ......................................................................................................... 132
if, else, elseif................................................................................................................... 132
while ............................................................................................................................... 133
do ... while ...................................................................................................................... 134
for ................................................................................................................................... 134
foreach ............................................................................................................................ 136
switch.............................................................................................................................. 136
break ............................................................................................................................... 137
continue .......................................................................................................................... 138
require() e include() ........................................................................................................ 138
Funciones............................................................................................................................ 140
Definición de funciones.................................................................................................. 140
Paso de parámetros ......................................................................................................... 140
Paso de parámetros por valor ......................................................................................... 140
Paso de parámetros por referencia.................................................................................. 141
Devolución de valores .................................................................................................... 142
Valores por defecto......................................................................................................... 142
Lista de argumentos de longitud variable....................................................................... 143
Funciones variables ........................................................................................................ 144
Clases y Objetos ................................................................................................................. 145
Algunas funciones de PHP ................................................................................................. 148
Funciones básicas ........................................................................................................... 148
echo y print ................................................................................................................. 148
Funciones de vectores..................................................................................................... 148
array() ......................................................................................................................... 148
El resto de funciones .................................................................................................. 149
Funciones de cadenas ..................................................................................................... 152
Funciones de variables ............................................................................... 153
Programación WEB............................................................................................................ 155
Cuenta de administración de la BD ................................................................................ 155
Inicialización y arranque de la BD ................................................................................. 155
Dando privilegios a la BD .............................................................................................. 156
Arranque del servidor ..................................................................................................... 156
Ejemplos ......................................................................................................................... 157
Creación archivo index.php............................................................................................ 157
Acceso a PostgreSQL ..................................................................................................... 158
Manejo de errores ............................................................................................................... 168
Observaciones..................................................................................................................... 170
Acerca de este libro
Aplicaciones WEB con PHP 4 y PosgreSQL 7.1 presenta
las principales características y forma de
instalación, administración y programación de las
aplicaciones WEB, a través de la arquitectura de tres
capas.

ARQUITECTURA DE 3 CAPAS

PostgreSQL
E
Lado
l

Del
e
n
Servidor
f
o
q
u Php
e

d
e

Lado
e Informes
s
Del
t
e
Cliente Pantalla
l
ibro se basa en el aprendizaje mediante ejemplos
completos que consolidan el aprovechamiento.
Presentación de la Obra
Estamos adquiriendo el compromiso con todos ustedes,
nuestros amigos lectores, de presentarles de manera
amena y agradable, las últimas novedades del mundo
informático del software libre.
Sabemos de sobra, que la computación e informática
son dos áreas del conocimiento humano contemporáneo
más dinámicas y cuyas expectativas siempre son de
vanguardia y que, por tanto, es difícil estar
actualizado. Con la llegada del software libre se dio
nuevo impulso a estas dos áreas. Linux es un sistema
operativo basado en los procesadores Intel. Ha sido
diseñado por Linus Torvalds y un ejercito de
programadores alrededor del mundo, cuyo es objetivo
es crear software libre de cualquier copy-right y que
el mundo entero pueda utilizar.
El dirigir una editorial de estas características es,
sin duda, el desafío más apasionante que puede
enfrentar un profesor, ya que permite difundir y
perpetuar nuestros conocimientos científicos y
tecnológicos, y ayudar al desarrollo y crecimiento
del individuo en, y para, beneficio de nuestra
sociedad.
Convenciones
En este libro aplicaremos las siguientes convenciones
para explicar el comportamiento de algunas de las
directivas de configuración y ejecución más
importantes. En rojo tenemos el mensaje proporcionado
por el sistema, en verde el resultado de la sentencia
y/o programa, así como direcciones a carpetas y/o
archivod. Utilizaremos azul en los ejemplos de
sentencias y código de programas completos.
Introducción a las
Aplicaciones WEB
En los últimos años le hemos perdido el miedo al
computador, de hecho cada día le exigimos más
potencia y prestaciones. La evolución de la
tecnología computacional influye en cómo dirigir a
las organizaciones, cómo establecer comunicaciones y
cómo realizar el trabajo. Pero esta tecnología no
sólo afecta al mundo empresarial, sino que también ha
permitido que la globalización nos envuelva y que
vivamos en la famosa aldea informática de McLuhan.
La arquitectura cliente/ servidor es una realidad, ya
que refleja nuestras necesidades, reales y virtuales,
como organización y cuya principal característica es
la de compartir recursos entre un servidor que
procesa requerimientos y un cliente que toma las
respuestas y opera con ellas; bajo este esquema que
cada parte integrante realiza lo que mejor sabe hacer
y a través de la web viajan las consultas, datos y
respuestas; además, este esquema exige un cambio en
la filosofía de nuestro trabajo. Precisamente cuando
múltiples nodos de esta arquitectura cliente/
servidor se unen, sin importar las distancias
geográficas, se forma una telaraña o WEB, en inglés,
o Internet.
Las aplicaciones que viajan entre el servidor WEB y
los clientes se denominan páginas WEB, las cuales son
archivos de texto ASCII escrito en lenguaje HTML;
inicialmente eran estáticas, en el sentido de que, a
efectos de usuario, el único proceso realizado era el
de visualización de sus contenidos por parte del
explorador del cliente. La transferencia se lleva a
cabo mediante el protocolo HTTP. Pero con el tiempo
se requiere de mayor participación e interacción de
los usuarios con sus datos para ofrecerle información
mejor dirigidas, seleccionadas y elaboradas, estas
últimas páginas se denominan páginas dinámicas.
Actualmente existen dos tipos de sitios Web: los que
se comportan como revistas, donde uno simplemente lee
la información ahí contenida (página estática) y los
que se comportan como el software, donde uno hace un
grupo de tareas específicas (página dinámica). Estas
últimas se conocen como aplicaciones Web, y son
desarrolladas por grupos de desarrollo de software,
tal como en las aplicaciones de escritorio.
Este último concepto nos conduce a las páginas WEB
activas en el servidor cuyos modelos son: ASP (active
server pedads) y CGI (common gateway interface).
El procesamiento de páginas dinámicas se clasifica
en:
- páginas dinámicas en el equipo del cliente.
- páginas activas en el servidor.
- páginas mixtas.
Una de las formas más populares para usar el WWW es a
través de las aplicaciones Web. Es la razón de la
aparición de un nuevo modelo de negocios en base a
estas aplicaciones: los ASP, los cuales ofrecen
masivamente el uso de sus aplicaciones a través del
WWW; y, por el otro lado el crecimiento de comercio
electrónico o e-commerce, en su orientación tanto B2B
como B2C.
La creciente popularidad de las aplicaciones WEB se
debe a sus múltiples ventajas, entre las cuales
podemos citar:
- Multiplataforma: Con un sólo programa, un único
ejecutable, las aplicaciones pueden ser utilizadas
a través de múltiples plataformas, tanto de
hardware como de software.
- Actualización instantánea: Debido que todos los
usuarios de la aplicación hacen uso de un sólo
programa que radica en el servidor, los usuarios
siempre utilizarán la versión más actualizada del
sistema.
- Suave curva de aprendizaje: Los usuarios, como
utilizan la aplicación a través de un navegador,
hacen uso del sistema tal como si estuvieran
navegando por Internet, por lo cual su acceso es
más intuitivo.
- Fácil de integrar con otros sistemas: Debido a que
se basa en protocolos estándares, la información
manejada por el sistema puede ser accedida con
mayor facilidad por otros sistemas.
- Acceso móvil: El usuario puede acceder a la
aplicación con la única restricción de que cuente
con un acceso a la red privada de la organización
o a Internet, dependiendo de las políticas de
dicha organización; puede hacerlo desde una
computadora de escritorio, una laptop o desde un
PDA; desde su oficina, hogar u otra parte del
mundo.
Además, uno de los servicios más importantes y usados
de Internet en la actualidad es el World Wide Web,
que ha evolucionado a pasos agigantados, primero
incorporando en el contenido de las páginas
diferentes tipos de letras, imágenes, sonidos etc. y
posteriormente, por medio del CGI, lenguajes script y
otros métodos, el usuario interactúa cada vez más con
el servidor Web: desde el envío de opiniones acerca
de una página determinada hasta el acceso y
administración de grandes bases de datos
empresariales.
El mejor servicio que en la actualidad ofrece el WEB
es que las organizaciones pongan a disposición del
mundo información acerca de los servicios y/o
productos que fabrican, o consultar sus actividades o
su situación financiera o de productividad o
disponibilidad de productos y/o servicios en el
momento y/o lugar deseado, así como establecer la
comunicación con las personas. El Web se ha
convertido en un escaparate de alcance mundial, donde
las organizaciones pueden mostrar sus objetivos a
prácticamente cualquier persona en el mundo a un
costo muy bajo.
En la actualidad existen diversos modelos de
desarrollo de aplicaciones Web cada uno con diversas
ventajas y desventajas. Estos modelos utilizan
diferentes tecnologías tanto de hardware como de
software, y tienen costos de licencias de uso muy
variados: desde las propuestas desarrolladas con
software que se puede utilizar de manera gratuita,
hasta los modelos que usan software con costo de
varios miles de dólares.
En este trabajo propongo el uso de una arquitectura
que utiliza tecnologías de software de uso libre, del
cual, su desempeño y confiabilidad ha sido
corroborado por millones de usuarios alrededor del
mundo gracias a Internet. Sin embargo, no existe un
compendio de documentación acerca de las tecnologías
e implementaciones existentes, así como de la manera
de integrarlas.
Este libro de divide en cuatro partes:
En la primera parte describiremos los requerimientos
de software para ejecutar nuestra aplicación WEB.
En la segunda, hablaremos de las tecnologías que
están involucradas en el desarrollo de aplicaciones
Web: Arquitectura Cliente/ Servidor, el HTTP, HTML,
lenguaje de consulta SQL y cifrado de datos. También
mencionaremos los diversos paradigmas que existen
para el desarrollo de aplicaciones Web: CGI, Co-
Servidor, e intérprete incrustado.
En la tercera parte, describiremos la estructura de
una aplicación Web y la problemática que se presenta
en su desarrollo. Se habla de la autenticación de
usuarios, manejo de sesiones, el uso de bitácoras,
acceso a datos, y el manejo de plantillas.
En la cuarta parte hablaremos acerca del diseño de
una aplicación Web, desde el análisis de
requerimientos, también describiremos algunos
lineamientos para el diseño de la interfaz de
usuario, y la creación de un prototipo.
Primera parte
Requerimientos
El objetivo de este libro es facilitar la instalación
de los servicios y programas necesarios para ejecutar
nuestras aplicaciones WEB en la mayoría de
distribuciones GNU/ Linux. Partimos de la base de que
tenemos una máquina con Linux1 instalado y
funcionando y con todas las herramientas necesarias
para la compilación de programas (gcc, make,..). Este
libro se sustentará en Apache 1.3.x, PHP 4.0.x y
PostgreSQL 7.1.x. Lo primero que tenemos que hacer es
bajar los tres paquetes con los programas necesarios
y grabarlos en un directorio de nuestro sistema (por
ejemplo, /local/download/):
- Bison
Principal
- Flex
Principal
- Apache
Principal
Mirrors
- PHP
Principal
- PostgreSQL
Principal
Mirrors
Primero verifiquemos que estas aplicaciones ya están
instaladas con los siguientes comandos:

1
El autor tiene instalado Linux Mandrake 8.0, quien ejecuto una aplicación personalizada tipo servidor,
seleccionó estas aplicaciones y fueron instaladas automáticamente con sus parámetros respectivos. Sólo
tenemos que verificar sus locaciones, las cueles cambian de acuerdo a la distribución de Linux que estemos
utilizando.
[localhost]$ flex --version

[localhost]$ bison --version

Sí obtenemos el número de versión, está instalada,


pero si el comando no es reconocido será necesarios
su instalación manual, la cual podemos realizar a
través de la secuencia de comandos:

BISON
Bájelo del site ftp://ftp.gnu.org/gnu/bison y
procedamos a ejecutar los siguientes comandos:

[localhost]$ tar -xzvf bison-1.28.tar.gz

[localhost]$ cd bison-1.28

[localhost]$./configure

[localhost]$ make

[localhost]$ make install

FLEX
Bájelo del site ftp://ftp.gnu.org/non-gnu/flex y
procedamos a ejecutar los siguientes comandos:

[localhost]$ tar -xzvf flex-2.5.4a.tar.gz

[localhost]$ cd flex-2.5.4a

[localhost]$./configure

[localhost]$ make
[localhost]$ make install

Para verificar su instalación satisfactoria, utilice


los comandos usados en su verificación.
Una vez que tenemos estos programas, elegimos la
carpeta donde los vamos a instalar manualmente a
Apache, PHP y Postgres. Por ejemplo, suponemos que
instalaremos en este orden:
Apache en: /usr/local/apache/
PHP como módulo de Apache
PostgreSQL en: /usr/local/pgsql/
Catalogo Web: /home/httpd/html/

Instalación de Apache/ Php


Procederemos como sigue:

[localhost]$ su

[localhost]$ cd /usr/src

[localhost]$ gunzip -c /local/download/apache_1.3.x.tar.gz

| tar xvf -

[localhost]$ gunzip -c /local/download/php-3.0.x.tar.gz

| tar xvf -

[localhost]$ cd apache_1.3.x

[localhost]$ ./configure --prefix=/usr/local/apache

[localhost]$ cd ../php-4.0.x

[localhost]$ ./configure --with-pgsql=/usr/local/pgsql

--with-apache=../apache_1.3.x --enable-track-vars
--enable-sysvsem --enable-sysvshm

--enable-url-includes

[localhost]$ make

[localhost]$ make install

[localhost]$ cd ../apache_1.3.x

[localhost]$ ./configure --prefix=/usr/local/apache

--activate-module=src/modules/php3/libphp3.a

[localhost]$ make

[localhost]$ make install

[localhost]$ cd ../php-4.0.x

[localhost]$ cp php4.ini-dist /usr/local/lib/php3.ini

[localhost]$ exit

Ya tenemos apache instalado y PHP como módulo del


mismo. Ahora tenemos que hacer unos cuantos ajustes
en el archivo de configuración para que todo
funcione. Tenemos que editar el archivo
/usr/local/apache/conf/httpd.conf y añadirle lo
siguiente:

Actualización del archivo de configuración


AddType application/x-httpd-php4 .php

DirectoryIndex index.html index.php


Estas dos líneas son las únicas que necesitamos para
que Apache sepa que hacer con un archivo que contenga
nuestro código PHP. Existen otras opciones que
deberíamos actualizar en nuestro archivo
/usr/local/apache/conf/httpd.conf para terminar de
configurar Apache, por ejemplo: ServerAdmin,
ServerName, DocumentRoot, directivas "Directory",
etc. Los comentarios incluidos en este archivo son
auto explicativos y no deberíamos tener ningún
problema para ajustar la configuración a nuestro
sistema. Ahora sólo nos queda arrancar el servidor
Apache:

Arranque del servidor


El servidor únicamente debe ser arrancado por el
super usuario:

[localhost]$ su

[localhost]$ /usr/local/apache/bin/httpd

-f /usr/local/apache/conf/httpd.conf

[localhost]$ exit

NOTA: Para obtener toda la información/ documentación


completa ubique la documentación Apache y PHP.

También, se inicializa el servicio httpd escribiendo:

[localhost]$ /usr/local/apache/bin/apachectl start

o inicializarlo automáticamente durante la


inicialización de GNU/ Linux, recordemos que esta
parte varia según la distribución.
Básicamente, creamos un archivo en el directorio
responsable de la inicialización (por ejemplo,
/etc/init.d/ ) que contenga a línea de comando previa
y lo hacemos ejecutable a través del comando:

[localhost]$ chmod +x <nombre del archivo>.


Instalación de PostgreSQL
Lo primero que tenemos que hacer es crear una cuenta
que administrará la base de datos:

Cuenta de administración de la BD
[localhost]$ su

[localhost]$ /usr/sbin/adduser postgres

[localhost]$ passwd postgres

[localhost]$ exit

Una vez creada la cuenta Postgres crearemos los


directorios que utilizaremos para instalar PostgreSQL
con los permisos adecuados:

Creación de directorios para la correcta


instalación de PostgreSQL
[localhost]$ su

[localhost]$ cd /usr/src

[localhost]$ mkdir pgsql

[localhost]$ chown postgres:postgres pgsql

[localhost]$ cd /usr/local

[localhost]$ mkdir pgsql

[localhost]$ chown postgres:postgres pgsql

[localhost]$ exit
Proceso de compilación/instalación
Empezamos con el proceso de compilación/instalación:

[localhost]$ su postgres

[localhost]$ cd /usr/src/pgsql

[localhost]$ gunzip -c /local/download/postgresql-6.5.x.tar.gz

| tar xvf -

[localhost]$ cd /usr/src/pgsql/postgresql-6.5.x/src

[localhost]$ ./configure --prefix=/usr/local/pgsql

--with-tcl --with-perl

[localhost]$ gmake all > make.log 2>&1 &

[localhost]$ tail -f make.log

[localhost]$ gmake install > make.install.log 2>&1 &

[localhost]$ tail -f make.install.log

[localhost]$ exit

Ahora tenemos que decirle al sistema donde puede


encontrar las librerías necesarias, para ello
actualizamos el archivo /etc/ld.so.conf:

Actualización del archivo de configuración


Empezamos con el proceso de configuración:

[localhost]$ su
[localhost]$ echo /usr/local/pgsql/lib >> /etc/ld.so.conf

[localhost]$ /sbin/ldconfig.

[localhost]$ exit

También tendremos que actualizar el archivo


~/.bash_profile de la cuenta administradora de la
base de datos, en este caso Postgres (si utilizas
otro shell que no sea bash, tienes que cambiar el
archivo correspondiente, en vez de .bash_profile):

Actualización del archivo profile


Debemos estar muy atento al archivo profile, para
ello, nos cambiamos como usuario postgres:

[localhost]$ su postgres

Editamos el archivo /etc/profile, o ~/.bash_profile,


y agregamos las siguientes declarativas, en caso de
que no existan:

PATH=$PATH:/usr/local/pgsql/bin

MANPATH=$MANPATH:/usr/local/pgsql/man

PGLIB=/usr/local/pgsql/lib

PGDATA=/usr/local/pgsql/data

export PATH MANPATH PGLIB PGDATA

export LC_COLLATE=C

export LC_CTYPE=C

Salir para que los cambios surtan efecto


[localhost]$ exit

Una vez que hemos terminado de instalar la base de


datos y configurar nuestro sistema, tenemos que
inicializarla y arrancarla:

Inicialización y arranque de la BD
Como usuario postgres, ejecutamos:

[localhost]$ su postgres

[localhost]$ initdb

o usamos la siguiente línea:

[localhost]$ /usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data

[localhost]$ cd

[localhost]$ nohup postmaster -i > pgserver.log 2>&1 &

o usamos la siguiente línea:

[localhost]$ /usr/local/pgsql/bin/postmaster -S -i -D /usr/local/pgsql/data

[localhost]$ exit

De modo similar a la instalación de Apache, podemos


crear un archivo para inicializar el servicio
PostgreSQL cuando se iniciemos el servidor o la
computadora. Recordemos que existen variantes entre
distribuciones GNU/ Linux, pero básicamente debemos
crear un archivo en el directorio responsable de la
inicialización (por ejemplo, /etc/init.d/ ) que
contenga:

echo Starting PostgreSQL ...


su postgres -c "/usr/local/pgsql/bin/postmaster -S -i -D
/usr/local/pgsql/data"

y lo convertimos en ejecutable con el comando:

# chmod +x <nombre de archivo>.

Ya tenemos nuestra base de datos PostgreSQL,


instalada y funcionando. Ahora sólo tenemos que
administrarla, para ello nada mejor que leerse los
manuales de documentación de la misma y aprender SQL.
Solamente nos queda hacer un par de ajustes en la
configuración para que podamos acceder a postgreSQL
vía PHP/ web:
- primero es incluir en el archivo
/usr/local/pgsql/data/pg_hba.conf la siguiente
línea: host all tu_maquina_IP tu_maquina_NETMASK
trust.
- segundo es dar privilegios de acceso en tu base de
datos/tablas al usuario "Nobody" para que pueda
recolectar los datos de la misma (Nobody es el
usuario que ejecuta el servidor Apache por
defecto).

Dando privilegios a la BD
Suponemos que tenemos una base de datos llamada
prueba, con una tabla llamada direcciones

[localhost]$ su postgres

[localhost]$ psql prueba

prueba=> GRANT SELECT ON direcciones

prueba=> TO nobody;

prueba=> \z

prueba=> \q
[localhost]$ exit

Instalación de PHP
Iniciaremos la instalación de PHP bajando el archivo
php-4.0.2.tar.gz del site http://www.php.net y
grabándolo en el directorio /usr/src/.
Ejecutamos los siguientes comandos:

[localhost]$ cd /usr/src

[localhost]$ tar -xzvf php-4.0.2.tar.gz

[localhost]$ cd /usr/src/php-4.0.2

[localhost]$. /configure --with-pgsql --with-


apxs=/usr/local/apache/bin/apxs

[localhost]$ make

[localhost]$ make install

Editamos el archivo httpd.conf con el siguiente


comando:

[localhost]$ emacs /usr/local/apache/conf/httpd.conf

quitamos las marcas de los comentarios, símbolo # en


el inicio de línea, y agregamos algunas extensiones
en las líneas que sean idénticas a esta:

AddType application/x-httpd-php .php .php3 .phtml

Quite el símbolo # de la línea

AddType application/x-httpd-php-source .phps

En el archivo httpd.conf, modifique DirectoryIndex


para que quede de la siguiente forma:
<IfModule mod_dir.c>

DirectoryIndex index.php index.php3 index.phtml index.html

</IfModule>

sólo nos queda copiar un archivo e reiniciar el


Apache. Ejecutamos el comando de copia:

[localhost]$ cp /usr/src/php-4.0.2/php.ini-dist /usr/local/lib

y el siguiente para reiniciar Apache:

[localhost]$ /usr/local/apache/bin/apachectl restart

por fin, ya tenemos nuestro Apache interpretando


páginas escritas con php.
Segunda Parte
PostgreSQL
El Sistema Gestor de Bases de Datos Relaciónales
Orientadas a Objetos conocido como PostgreSQL
(denominado Postgres95) está derivado del paquete
Postgres escrito en la Universidad de California ,
Berkeley. Con cerca de una década de desarrollo tras
él, PostgreSQL es el gestor de bases de datos de
código abierto más avanzado hoy en día, ofreciendo
control de concurrencia multiversión, soportando casi
toda la sintaxis SQL (incluyendo subconsultas,
transacciones, y tipos y funciones definidas por el
usuario), contando también con un amplio conjunto de
enlaces con lenguajes de programación (como Kylix,
Free Pascal, C, C++, Java, perl, tcl y python). Sus
limitaciones técnicas casi no existen porque puede
manejar una tabla que contenga hasta 64 tera octetos
de datos.

El proyecto Postgres de Berkeley


La implementación del DBMS Postgres comenzó en 1986.
las cuatro etapas históricas son: a) los conceptos
iniciales para el sistema, b) la definición del
modelo de datos inicial, c) el diseño del sistema de
reglas y d) la lógica y arquitectura del gestor de
almacenamiento.
Postgres ha pasado por varias revisiones importantes
desde entonces. El primer sistema operacional de
pruebas estuvo listo en 1987 y fue mostrado en la
Conferencia ACM-SIGMOD de 1988. Unos pocos usuarios
externos disfrutaron de la Versión 1 en Junio de
1989. Debido a una serie de críticas del primer
sistema de reglas, éste fue rediseñado y tuvo
consecuencia la Versión 2, que salió en Junio de
1990. La Versión 3 apareció en 1991 y añadió una
implementación para múltiples gestores de
almacenamiento, un ejecutor de consultas mejorado y
un sistema de reescritura de reglas nuevo. En su
mayor parte, las siguientes versiones hasta el
lanzamiento de Postgres95 se centraron en mejorar la
portabilidad y la fiabilidad.
Postgres forma parte de la implementación de muchas
aplicaciones de investigación y producción. Entre
ellas: un sistema de análisis de datos financieros,
un paquete de monitorización de rendimiento de
motores a reacción, una base de datos de seguimiento
de asteroides y varios sistemas de información
geográfica. También se ha utilizado como una
herramienta educativa en varias universidades.
Finalmente, Illustra Information Technologies
(posteriormente absorbida por Informix) tomó el
código y lo comercializó. Postgres llegó a ser el
principal gestor de datos para el proyecto científico
de computación Sequoia 2000 a finales de 1992.
La comunidad de usuarios postgres se duplicó durante
1993. Pronto se hizo obvio que el mantenimiento del
código y las tareas de soporte estaban ocupando
tiempo que debía dedicarse a la investigación. En un
esfuerzo por reducir esta carga, el proyecto terminó
oficialmente con la Versión 4.2.

Postgres95
En 1994, Andrew Yu y Jolly Chen añadieron un
intérprete de lenguaje SQL a Postgres. A
continuación, Postgres95 fue publicado en la Web para
que encontrara su propio nicho en el mundo
informático como un descendiente de dominio público y
código abierto del código original Postgres de
Berkeley.
El código de Postgres95 fue adaptado a ANSI C y su
tamaño reducido en un 25%. Muchos cambios internos
mejoraron el rendimiento y la facilidad de
mantenimiento. Postgres95 v1.0.x se ejecutaba en
torno a un 30-50% más rápido en el Wisconsin
Benchmark comparado con Postgres v4.2. Además de
corrección de errores, éstas fueron las principales
mejoras:
• El lenguaje de consultas Postquel fue reemplazado
con SQL (implementado en el servidor). Las
subconsultas no fueron soportadas hasta PostgreSQL
(ver más abajo), pero podían ser emuladas en
Postgres95 con funciones SQL definidas por el
usuario. Las funciones agregadas fueron
reimplementadas. También se añadió una
implementación de la cláusula GROUP BY. La
interfaz libpq permaneció disponible para
programas escritos en C.
• Además del programa de monitorización, se incluyó
un nuevo programa (psql) para realizar consultas
SQL interactivas usando la librería GNU readline.
• Una nueva librería de interfaz, libpgtcl,
soportaba clientes basados en Tcl. Un shell de
ejemplo, pgtclsh, aportaba nuevas órdenes Tcl para
interactuar con el motor Postgres95 desde
programas tcl.
• Se revisó la interfaz con objetos grandes. Los
objetos grandes de Inversion fueron el único
mecanismo para almacenar objetos grandes (el
sistema de archivos de Inversion fue eliminado).
• Se eliminó también el sistema de reglas a nivel de
instancia, si bien las reglas siguieron
disponibles como reglas de reescritura.
• Se distribuyó con el código fuente un breve
tutorial introduciendo las características comunes
de SQL y de Postgres95.
• Se utilizó GNU make (en vez de BSD make) para la
compilación. Postgres95 también podía ser
compilado con un gcc sin parches (al haberse
corregido el problema de alineación de variables
de longitud doble).

PostgreSQL
En 1996, se hizo evidente que el nombre "Postgres95"
no resistiría el paso del tiempo. Se selecciono como
nuevo nombre, PostgreSQL, para reflejar la relación
entre el Postgres original y las versiones más
recientes con capacidades SQL. Al mismo tiempo, se
logro que los números de versión partieran de la 6.0,
volviendo a la secuencia seguida originalmente por el
proyecto Postgres.
Durante el desarrollo de Postgres95 se hizo hincapié
en identificar y entender los problemas en el código
del motor de datos. Con PostgreSQL, el énfasis ha
pasado a aumentar características y capacidades,
aunque el trabajo continúa en todas las áreas.
Las principales mejoras en PostgreSQL incluyen:
• Los bloqueos de tabla han sido sustituidos por el
control de concurrencia multiversión, el cual
permite a los accesos de sólo lectura continuar
leyendo datos consistentes durante la
actualización de registros, y permite copias de
seguridad en caliente desde pg_dump mientras la
base de datos permanece disponible para consultas.
• Se han implementado importantes características
del motor de datos, incluyendo subconsultas,
valores por defecto, restricciones a valores en
los campos (constraints) y disparadores
(triggers).
• Se han añadido funcionalidades en línea con el
estándar SQL92, incluyendo claves primarias,
identificadores entrecomillados, forzado de tipos
cadena literales, conversión de tipos y entrada de
enteros binario y hexadecimal.
• Los tipos internos han sido mejorados, incluyendo
nuevos tipos de fecha/ hora de rango amplio y
soporte para tipos geométricos adicionales.
• La velocidad del código del motor de datos ha sido
incrementada aproximadamente en un 20-40%, y su
tiempo de arranque ha bajado el 80% desde que la
versión 6.0 fue lanzada.

Concurrencia de acceso para multiversión


En la siguiente escenario, típico de los SGBD
tradicionales, tenemos un servidor operativo y una
base de datos, prueba, corriendo. Si en una primera
estación de trabajo, conectada al servidor,
ejecutamos las siguientes sentencias:

Prueba=# create table prueba1(a integer);

CREATE

Prueba=# insert into prueba1 values(1);


INSERT INTO 5432 1

Prueba=# begin transaction;

BEGIN

Prueba=# update prueba1 set a=1;

UPDATE 1

Prueba=#

En este momento, una segunda terminal de trabajo,


también conectada al mismo servidor, ejecuta:

Prueba=# select * from prueba1;

Sentencia que no se ejecuta porque ¡la terminal está


bloqueada! Select permanece en espera hasta que
reciba una respuesta de la base de datos de que se
puede ejecutar. La explicación a esto es muy
sencilla, ya que colocamos una acción de exclusividad
en una operación de escritura (UPDATE) con una
transacción (BEGIN TRANSACTION). En consecuencia la
operación de lectura permanecerá bloqueada hasta que
la acción de exclusividad sea liberada, lo que ocurre
cuando alcanzamos el fin de la transacción. Hecho que
se manifiesta cuando se encuentra un COMMIT o un
ROLLBACK.
Con PostgreSQL, la segunda terminal no estará
bloqueada. Con el comando select tendremos
inmediatamente una respuesta:
A
--
1
(1 row)

esto se explica por el mecanismo de concurrencia de


acceso para multiversión, aunque la primera
transacción de la primera terminal no esté terminada,
la segunda puede consultar la base de datos. Si la
segunda terminal ejecuta una transacción, y busca
modificar el mismo registro, será bloqueada de la
misma forma que en una base de datos tradicional.

Herencia, estructura orientada a objetos


Postgres une lo mejor de dos mundos; es una base de
datos relacional, pero también es una base de datos
orientada a objetos. Permite tener estructuras
comunes a tablas físicas, lo que facilita el
mantenimiento del modelo físico, pero igualmente
tiene una noción de tablas jerárquicas. Estudiemos el
siguiente ejemplo:

Prueba=# create table prospectos(nombre varchar);

CREATE

Prueba=# create table clientes(fechapedido date) inherits (prospectos);

CREATE

Prueba=# insert into prospectos values(‘LinusFrench’);

INSERT 5432 1

Prueba=# select * from prospectos;

Nombre
---
LinuxFrench
(1 row)

Prueba=# select * from prospectos;

nombre
-------------
LinuxFrench
LinuxFrench
(2 rows)

Prueba=# select * from ONLY prospectos;

nombre
-------------
LinuxFrench
(1 row)
En este ejemplo tenemos una tabla "clientes" que
hereda de la tabla "prospectos". Si tenemos una línea
en cada una de las tablas, podemos obtener un cliente
mientras consultamos la tabla clientes, por el otro
lado, si consultamos la tabla prospectos, tendremos
bien sea el conjunto de los prospectos y el de los
clientes (una línea de cada uno de nuestro ejemplo),
sólo únicamente los prospectos utilizando la cláusula
ONLY en el SELECT.

Procedimientos almacenados
PostgreSQL permite implementar procedimientos
almacenados como funciones. Estos procedimientos
pueden ser escritos en diferentes leguajes de
programación, entre ellos:
• SQL "estándar" (SQL92).
• PL/pgSQL, que es un lenguaje procedimental
directamente derivado de SQL.
• C.
• Tcl.
• Perl.
• Python.
• Ruby.
Me gusta utilizar PL/pgSQL por su proximidad al
PL/SQL de Oracle.
También debemos apreciar que postgres nos permite
escribir funciones distintas con el mismo nombre,
pero tomando diferentes parámetros, lo cual es mejor
que escribir muchas funciones diferentes. Veamos el
ejemplo siguiente:
• suprimirCliente(integer).
• suprimerCliente(varchar).
en lugar de:
• suprimerClientePorIdentificador(integer).
• suprimerClientePorNombre(varchar).
Sin embargo, existe una limitación importante (si,
PostgreSQL no es perfecto!), es imposible abrir o
cerrar una transacción desde un procedimiento
almacenado. De hecho, postgres no maneja
transacciones empotradas. Si nuestra aplicación lo
hace, no podemos utilizar "BEGIN TRANSACTION".

Triggers, constricciones
Un procedimiento almacenado (o función en la
terminología postgres), puede ser llamado desde un
trigger (también llamado disparador). Un trigger es
una operación que será ejecutada durante el acceso a
una tabla, también puede ser llamado desde un SELECT,
INSERT y/o UPDATE. El llamado puede ser realizado
desde una línea de comando o por una sentencia SQL.
Además, un trigger puede ejecutarse antes o después
de la manipulación de los datos para que sea
realmente efectivo. Son muy prácticos porque efectúan
verificaciones de coherencia o de integridad de los
datos antes de permitir una modificación sobre la
tabla. Igualmente pueden servir para efectuar
operaciones más complejas como por grabar en una
tabla los datos antiguos antes de que sean
modificados para generar las distintas versiones
sucesivas para la auditoria.
PostgreSQL también maneja constricciones, las cuales
son declaradas durante el proceso de creación de la
tabla. Tenemos las constricciones clásicas: NULL/NO
NULL, UNIQUE, PRIMARY KEY y REFENCES (clave foránea)
así como CHECK aunque es menos común. Esta
constricción nos permite escribir una expresión
booleana que acepte o no ciertos valores. Por ejemplo
queremos verificar que haya un mínimo de dos campos
de una línea que pueden ser reasignados (TEL_FIJO o
TEL_GSM). En la declaración de una constricción con
respecto a una clave foránea, podemos especificar el
comportamiento en caso de eliminación del dato
referenciado (rechazar, eliminar en cascada, pasar la
clave foránea a NULL o al valor por defecto definido
para esa columna). ¡Pruébelo!
PostgreSQL es una base de datos muy poderosa. El
autor tiene muchos años programando en base de datos
propietarias (Oracle, SQL Server 7 y Access) en
diferentes y variadas aplicaciones (Bancos, sistemas
de control de estudios, sistemas administrativos y
bases documentales...), y debo decirles que sólo
extraño una cosa en el pgSQL en las aplicaciones muy
críticas: las transacciones incorporadas en los
procedimientos almacenados. También puede encontrar
unas pocas fallas, por ejemplo los procedimientos
almacenados no pueden recuperar algunos registros.
Aunque la misma limitante existe en Oracle. En el
término de rendimiento, podemos decir que globalmente
pgSQL soporta muy bien la carga y que realmente se
adapta muy bien a un servidor intranet.

Introducción, tablas, filas y columnas


Las bases de datos invaden nuestra vida privada y son
incontrolables en la informática, a continuación
presentamos una serie de apartados con el objetivo de
incrementar nuestra comprensión, utilización y
concepción de las bases de datos.

Un poco de vocabulario

Como todo mundo sabe que una base de datos (bd) –


database en inglés- es un sistema informático
permanente de escritura, almacenaje, acceso y
recuperación de datos. Fundamentalmente, la noción de
base de datos es muy amplia. Por ejemplo, un archivo
contenedor de su edadnda telefónica proveniente de un
procesador de palabras ya es en sí una base de datos;
lo que distingue una base de datos de otra es su
estructura. Nuestra edadnda telefónica no tiene, por
decirlo, una estructura aunque se aloje en nuestro
disco duro. Por otra parte, cuando enviamos un correo
electrónico, éste se comporta como una base de datos
estructurada, debido a que se almacena por ciertos
campos, como por ejemplo: remitente, destinatario,
fecha de expedición, lugar, texto, etc.
Ahora bien, un sistema de gestión de base de datos,
SGBD, se refiere a un sistema informático
expresamente concebido para la gestión de datos, la
mayoría de las veces estructurados.
Las bases de datos relacionales son aquellas que
establecen las relaciones entre entidades diferentes.
Por ejemplo, si tenemos un diccionario y un atlas;
dentro del diccionario encontramos y leemos, el Museo
de Antropolgía e Historia, museo mexicano, situado en
Ciudad de México, D.F. cf: atlas, Ciudad de México,
p.230. ya tenemos una relación entre dos entidades,
el diccionario y el atlas, al leer un datos del
diccionario recuperamos información que nos permite
acceder a un dato en la otra entidad, el atlas.
Recordamos que no solamente existen base de datos
relacionales, también existen las jerárquicas y las
orientadas a objetos.
Para acceder a los datos dentro de un SGBDR,
utilizamos el lenguaje SQL (Structured Query
Languedad o Lenguaje estructurado de consultas) y es
un lenguaje procedimental. Es un hecho sobre una
transposición de computación de los funcionamientos
algebraicos como un todo. En un SGBDR los datos están
completos y podemos recuperar uno diciendo por
ejemplo "quiero la unión entre los clientes que
compraron más de 3 veces este año, y aquellos que han
gastado un total superior a Bs. 10.000,00 lo que es
completamente diferente a decir acerca de la
intersección entre los clientes que compraron más de
3 veces este año y aquellos que gastaron más de Bs.
10.000,00 (en el primer caso tenemos un OR, mientras
que en el segundo un AND).

Las tablas

En un SGDBR, los datos son almacenados en tablas,


veamos el siguiente ejemplo:

lact_code | lact_lib
-------------+---------------------------------------------
15 | Industries alimentaires
16 | Industrie du tabac
17 | Industrie textile
18 | Industrie de l’habillement et des fourrures
19 | Industrie du cuir et de la chaussure

en esta tabla tenemos 5 filas o tuplas y dos


columnas, lact_code y lact_lib. Al hablar de columnas
podemos utilizar como sinónimo campos, cada campo
tiene un tipo particular, en nuestro ejemplo, el
primero es de tipo numérico entero y el segundo es
una cadena de caracteres.

NULL, un valor a parte

Dentro de una base de datos, y en especifico dentro


de cada tabla, existe un valor particular que se
llama NULL o indeterminado.
Atención, no significa vacío, es diferente, si
tenemos una tabla con el campo número_de_hijos y
asignamos un 0, significa que no tenemos hijos, pero
si no reasignamos el campo tendremos NULL o
indeterminado, igualmente pasa con el campo apellido,
si contiene el valor NULL es indeterminado el
apellido, pero si asignamos vacío, la persona no
tiene apellido. Por lo que la cadena vacío y el valor
NULL son valores completamente diferentes,
indeterminado y determinado.
El valor NULL no es un tipo de dato y no puede
realizar ninguna operación aritmética porque es
indeterminado. 1 + NULL genera NULL. La concatenación
de una cadena a un NULL también produce un NULL.

Prueba1=> SELECT * FROM alumnos;

id | nom | apellidos | edad | clase


----+--------+---------------+-------+--------
7
5 | MARTIN | Philippe | |
6 | TUX | Family | |
(2 rows)
CREATE TABLE, SELECT, INSERT,
DELETE
Asumimos que ya tenemos instaldos Linux y PostgreSQL
en nuestra computadora.

Creación de tablas

Una tabla se crea con el comando SQL CREATE TABLE. Si


queremos cear la tabla alumnos, compuesta de 3
columnas: nombre, apellido y edad, ejecutamos:

Prueba1=# CREATE TABLE alumnos (

Prueba1 (# nom varchar,

Prueba1 (# apellidos varchar,

Prueba1 (# edad integer);

CREATE

Prueba1=#

El comando puede estar en una sola línea, pero esto


no es importante ya que el comando no se ejecuta
hasta que se encuentra un ; (punto y coma) que indica
el fin de sentencia. Después del comando escribimos
el nombre de la tabla (alumnos) y entre paréntesis
la lista de los campos. Para cada campo debemos
especificar su nombre y tipo. En nuestro ejemplo
tenemos 2 tipos de campo, integer que es un entero
con signo y que ocupa 32 bits, varchar que es una
cadena de caracteres de longitud variable. Debemos
recordar que PostgreSQL es sensible a mayúsculas y
minúsculas, por lo que Create Table alumnos, produce
el mismo resultado, pero así debemos escribir el
nombre la tabla para todas sus referencias
siguientes.

Adición y consulta de datos

Para insertar datos en nuestra tabla utilizamos el


comando INSERT INTO.

Prueba1=# INSERT INTO alumnos VALUES(’DUPONT’,’Martin’,25);

INSERT 53638 1

Prueba1=#

El valor 53638 que asigna PostgreSQL cuando el


comando es ejecutado corresponde al OID que se
atribuye al nuevo registro (oid es un identificador
numérico único que corresponde a cada objeto dentro
de PostgreSQL) debemos estar atentos a que el número
que aparezca en nuestra aplicación puede ser
diferente. El número que le sigue indica que el
comando INSERT afecto a un solo registro. Después
oodemos consultar los datos con el comando SELECT:

Prueba1=# SELECT nom FROM alumnos;

nom
--------
DUPONT
(1 row)

Prueba1=# SELECT nom, apellidos FROM alumnos;

nom | apellidos
--------+--------
DUPONT | Martin
(1 row)

Prueba1=# SELECT * FROM alumnos;


nom | apellidos | edad
--------+-----------+-----
DUPONT | Martin | 25
(1 row)

Prueba1=#

El argumento * permite seleccionar todos los


registros y todas sus filas, representa una ganancia
en la codificación de una aplicación y recupera las
columnas en el mismo orden en que fueron declaradas,
aunque debe ser manejado con cuidado.
Un SELECT igualmente puede utilizarse para ejecutar
operaciones, por ejemplo, el operador « || »
efectuará una concatenación de dos cadenas de
caracteres:

Prueba1=# SELECT nom||’ ’||apellidos AS np, nom AS name FROM


alumnos;

np | name
---------------+--------
DUPONT Martin | DUPONT
9
(1 row)

Prueba1=#

Aquí cambiamos el nombre de referencia de una columna


con AS, recomendamos esta buena práctica en
operaciones complejas y cuyo resultado necesitemos
mostrar u operar con él posteriormente. Este técnica
se conoce como asignación de ALIAS a una columna.

Operaciones con cursores

Como hemos visto, SQL permite manipular cursores. ¿Un


ejemplo? Las sentencias INSERT y SELECT. SELECT
regresa un cursor con datos editados de una o más
tablas, mientras que INSERT adiciona un cursor de
datos en una tabla. Analicemos el siguiente caso:

Prueba1=# SELECT * FROM alumnos;

nom | apellidos | edad


--------+--------+-----
DUPONT | Martin | 25
(1 row)

Prueba1=# INSERT INTO alumnos SELECT * FROM alumnos;

INSERT 53639 1

Prueba1=# SELECT * FROM alumnos;

nom | apellidos | edad


--------+-----------+-----
DUPONT | Martin | 25
DUPONT | Martin | 25
(2 rows)

Prueba1=#

En el primer caso, obtenemos un cursor con un


registro, el único que existe en la tabla. En el
segundo, insertamos un cursor de un registro en la
tabla, en el tercer caso, obtenemos un cursor con dos
registros.

Haciendo limpieza

Para suprimir datos de una tabla, ejecutamos la


sentencia DELETE:

Prueba1=# DELETE FROM alumnos;

DELETE 2
Prueba1=\#

Con ella suprimimos permanentemente los dos registros


de la tabla alumnos. No hay forma de recuperarlos, a
menos que nuevamente sean adicionados a nuestra
tabla. También podemos suprimir definitivamente la
tabla alumnos, para recuperarla debemos crearla
nuevamente con CREATE TABLE:

Prueba1=# DROP TABLE alumnos;

DROP

Prueba1=\#

WHERE y UPDATE
En el apartado anterior vimos la forma de recuperar
registros en las tablas, pero las formas empleadas
devolvían todos los registros de la mencionada tabla.
Ahora estudiaremos las posibilidades de filtrar los
registros con el fin de recuperar o actualizar
aquellos que cumplan con ciertas condiciones
preestablecidas.

La cláusula WHERE

La cláusula WHERE se usa para determinar qué


registros de las tablas enumeradas en la cláusula
FROM aparecerán en los resultados de la instrucción
SELECT. WHERE restringe o selecciona un grupo de
registros que cumplan con cierta condición y siempre
regresa un valor booleano. Analicemos el siguiente
ejemplo:

Prueba1=# CREATE TABLE alumnos(nom varchar,


apellidos varchar, edad integer, clase char(4));

CREATE
Prueba1=# INSERT INTO alumnos VALUES(’DUPONT’, ’Martin’, 6,
’CP’);

INSERT 53698 1

Prueba1=# INSERT INTO alumnos VALUES(’DURAND’, ’Theo’,15,


’3A’);

INSERT 53699 1

Prueba1=# INSERT INTO alumnos VALUES(’DUPOND’, ’Léa’,12, ’6B’);

INSERT 53700 1

Prueba1=# SELECT {*} FROM alumnos WHERE edad > 10;

nom | apellidos | edad | clase


--------+-----------+------+--------
DURAND | Theo | 15 | 3A
DUPOND | Léa | 12 | 6B
(2 rows)

Prueba1=#

Si observamos con atención el proceso de creación de


la tabla, notaremos un nuevo tipo de dato
incorporado: char(4). Lo que significa que el campo
tiene una longitud fija de 4 caracteres, al asignarle
‘6B’ el SGDB adiciona 2 espacios, al derecha, para
completar la cadena, pero si sobrepasamos la cadena,
sólo toma los cuatro primeros caracteres. Después
insertamos tres registros en nuestra tabla y por
último ejecutamos una sentencia SELECT con una
cláusula WHERE edad > 10 .
Esto significa que nuestra sentencia SQL mostrará
solamente aquellos registros cuyo campo edad (edad)
es estrictamente mayor que 10.
Podemos utilizar el operador Like en una expresión
para comparar un valor de un campo con una expresión
de cadena. Veamos el siguiente ejemplo:

Prueba1=# SELECT {*} FROM alumnos WHERE nom LIKE ’%PO%’;

nom | apellidos | edad | clase


--------+------------+------+--------
DUPONT | Martin |6 | CP
DUPOND | Léa | 12 | 6B
(2 rows)

Notemos la comilla simple y el símbolo de % de


porcentaje.
También podemos emplear cualquiera de los operadores
lógicos en nuestra sentencia, analicemos la sentencia
siguiente:

Prueba1=# SELECT * FROM alumnos WHERE nom LIKE ’DU%’ AND


clase=’CP ’

nom | apellidos | edad | clase


--------+-----------+------+--------
DUPONT | Martin | 6 | CP
(1 row)

UPDATE

Con este comando creamos una consulta de


actualización que cambia los valores de los campos de
una tabla especificada basándose en un criterio
único. Por ejemplo, si queremos corregir la edad y
clase del alumno DUPONT:

Prueba1=# UPDATE alumnos SET edad=16, clase=’3A’ WHERE


nom=’DUPONT’;

UPDATE 1
Prueba1=# select * from alumnos;

nom | apellidos | edad | clase


--------+-----------+------+--------
DURAND | Theo | 15 | 3A
DUPOND | Léa | 12 | 6B
DUPONT | Martin | 16 | 3A
(3 rows)

Prueba1=#

DISTINCT e INTO

Omite los registros que contienen datos duplicados en


los campos seleccionados. Para que los valores de
cada campo listado en la instrucción SELECT se
incluyan en la consulta deben ser únicos. Analicemos
los dos ejemplos siguientes:

Prueba1=# SELECT clase FROM alumnos;

clase
--------
3A
6B
3A
(3 rows)

que nos muestra campos repetidos; mientras que:

Prueba1=# SELECT DISTINCT clase FROM alumnos;

clase
--------
3A
6B
(2 rows)
también podemos utilizar esta sentencias para crear
una nueva tabla que contenga la lista de las clases,
para ello empleamos la cláusula INTO que se comporta
como un direccionador en la línea de comandos Linux o
Windows:

Prueba1=# SELECT DISTINCT clase INTO lista_clases FROM


alumnos;

SELECT

Prueba1=# select * from lista_clases;

clase
--------
3A
6B
(2 rows)

es imperativo para ejecutar el comando que la tabla


destino liste_clase no exista!

Índices, claves y join


Hemos aprendido a crear y manipular cursores de
datos. Al hablar de SGDBR, donde la R significa
relacional, por lo que debemos aprender a establecer
dichas relaciones.

Índices

Los índices tiene por objeto acelerar el acceso a sus


datos. Si buscamos un valor en una tabla, como la
lista de los alumnos con una edad igual a 10,
escribiríamos:

SELECT * FROM alumnos WHERE edad=10;


El SGDBR recorre la tabla y extrae los registros que
cumplen con la condición impuesta por la cláusula
WHERE.
Si consideramos que el tiempo de búsqueda es
directamente proporcional al número de registros
contenidos en la tabla. Si duplicamos el número de
registros, entonces el tiempo de búsqueda será el
doble. Si la cláusula FROM contiene 2 tablas, el
tiempo de búsqueda será multiplicado por 4.
Debemos decir que estas perspectivas no son
halagadoras para una base de datos. Afortunadamente
podemos colocar índices sobre uno o más campos de una
tabla, ya que funcionan de la misma forma que un
índice de un libro, en lugar de buscar en el libro
completo, buscamos la entrada correspondiente en el
índice que nos indique directamente el número de
página a leer.
Un índice en PostgreSQL es, por lo general, un B-
TREE, es decir, un árbol equilibrado y aunque
PostgreSQL permite otros tipos de índices, el tipo
por defecto es un B-TREE. El SGDBR almacena una
representación ramificada de valores. Cada rama
(nodo) tiene un valor, si el valor que buscamos es
mayor al del nodo, nos movemos a la derecha, en caso
contrario a la izquierda. Las hojas del árbol dan
directamente la dirección de la línea concerniente.
Si tenemos una tabla con 1000 registros, y el árbol
es binario y equilibrado, podremos buscar registros
en solamente 10 comparaciones (recordemos que 2
elevado a la 10 es igual a 1024) en lugar de las 1000
propuestas. Si duplicamos el número de registros no
duplicamos el número de búsquedas, es 11!
Para crear un índice sobre nuestra tabla alumnos con
el propósito de acelerar nuestras búsquedas empleamos
el comando CREATE INDEX:

Prueba1=# CREATE INDEX idx_eleve_edad ON alumnos(edad);

CREATE

Prueba1=#

un índice puede contener más de un campos. Si esto se


presente, el valor de clave será calculado por el
SGDB después de concatenar el valor de los campos.
Vamos un índice conteniendo nom y apellidos:

CREATE INDEX idx_eleve_nom ON alumnos(nom,apellidos);

De nuestro ejemplo, debemos considerar que si hacemos


una búsqueda por el nombre del alumno, el índice será
de gran utilidad y la búsqueda es rápida. Esto
funcionará de la misma forma si buscamos por el
nombre y apellido, es decir, nombre completo, pero si
buscamos sólo por el apellido el índice ya no es
útil. En efecto, la clave del índice está calculada
en función de nombre y apellido según el comando
CREATE INDEX, además de que el orden de los campos es
muy importante.
Un índice pude servir para verificar la unicidad de
un valor. Ahora hablamos de clave, si no existen
homónimos podemos crear un índice único con
NOM+APELLIDOS:

CREATE UNIQUE INDEX pk_alumno_nom ON alumnos(nom,apellidos);

Ahora que ya tenemos el índice, todo intento de


insertar dos veces el mismo alumno en la tabla no
será permitido!
Antes de continuar, es importante señalar que si bien
el índice acelera el acceso a sus datos, las
operaciones de escritura son lentas porque debe
mantenerse el árbol equilibrado.

Join

Creemos una nueva tabla, denominada profesores:

Prueba1=# CREATE TABLE profesores(clase char(4), nom varchar);

CREATE

Prueba1=# INSERT INTO profesores VALUES(’3A’,’M. Hiboo’);

INSERT 5053839 1
Prueba1=# INSERT INTO profesores VALUES(’6B’,’Mme Grenouille’);

INSERT 5053840 1

Si queremos la lista de alumnos con el nombre del


profesor que da la clase, quizá nuestra idea sea:

Prueba1=# SELECT * FROM alumnos, profesores;

nom | apellidos | edad | clase | clase | nom


--------+-----------+------+-------+---------+----------------
DURAND | Theo | 15 | 3A | 3A | M. Hiboo
DURAND | Theo | 15 | 3A | 6B | Mme Grenouille
DUPOND | Léa | 12 | 6B | 3A | M. Hiboo
DUPOND | Léa | 12 | 6B | 6B | Mme Grenouille
DUPONT | Martin | 16 | 3A | 3A | M. Hiboo
DUPONT | Martin | 16 | 3A | 6B | Mme Grenouille
(6 rows)

Prueba1=#

¿Qué paso? Que hemos efectuado un producto cartesiano


entre dos cursores que constituyen las tablas.
Diremos que por cada registro de la tabla alumnos y
cada uno de la tabla profesores, SELECT produce una
tupla constituida por alumnos y profesores. Hemos
obtenido todas las combinaciones posibles de alumnos
y profesores! Cosa muy interesante pero no es el
resultado que deseamos. La operación que requerimos
se llama JOIN (unión), por cada alumno, requerimos el
profesor que da la clase. Cada tipo de unión se
denomina unión interna.
Tenemos dos formas de hacerlo, la primera es
utilizando la cláusula WHERE especificando la
igualdad entre los campos clase de ambas tablas:

Prueba1=# SELECT alumnos.nom, alumnos.clase, profesores.nom

Prueba1=# FROM alumnos, profesores

Prueba1=# WHERE alumnos.clase=profesores.clase;


nom | clase | nom
--------+--------+----------------
DURAND | 3A | M. Hiboo
DUPONT | 3A | M. Hiboo
DUPOND | 6B | Mme Grenouille
(3 rows)

Prueba1=#

el otro método es utilizar el comando JOIN . este


método es preferible a fin de permitir las uniones
externas, ya que impone dependencia sobre el campo
servidor de clave dentro de cada una de las tablas
con el mismo nombre, analicemos:

Prueba1=# SELECT alumnos.nom, alumnos.clase, profesores.nom

Prueba1=# FROM alumnos JOIN profesores USING (clase);

nom | clase | nom


--------+--------+----------------
DURAND | 3A | M. Hiboo
DUPONT | 3A | M. Hiboo
DUPOND | 6B | Mme Grenouille
(3 rows)

Prueba1=#

Hemos obtenido el mismo resultado que con la cláusula


WHERE. He aquí el motivo de las bases de datos
relacionales, las relaciones establecidas entre las
diferentes tablas se basan en las claves.
Si añadimos un nuevo alumno sin afectar su clase
(campo clase NULL) y hacemos un join interno como
procedimiento y luego un join externo, tenemos:

Prueba1=# INSERT INTO alumnos VALUES(’FAMILY’,’Tux’,10,null);

INSERT 5053897 1

Prueba1=# SELECT alumnos.nom, alumnos.clase, profesores.nom

Prueba1=# FROM alumnos JOIN profesores USING (clase);


nom | clase | nom
--------+--------+----------------
DURAND | 3A | M. Hiboo
DUPONT | 3A | M. Hiboo
DUPOND | 6B | Mme Grenouille
(3 rows)

Prueba1=# SELECT alumnos.nom, alumnos.clase, profesores.nom

Prueba1=# FROM alumnos LEFT OUTER JOIN profesores USING


(clase);

nom | clase | nom


--------+-------+----------------
DURAND | 3A | M. Hiboo
DUPONT | 3A | M. Hiboo
DUPOND | 6B | Mme Grenouille
FAMILY | |
(4 rows)

Prueba1=#

Con el primer join interno, el nuevo alumno no


aparece en la lista del profesor; mientras que con el
join externo si aparece. LEFT indica que la tabla que
se encuentra a la izquierda de la expresión (alumnos)
es susceptible a tener claves tipo NULL.

Los agregados
Empleamos a las funciones de agregado dentro de una
cláusula SELECT en grupos de registros para devolver
un único valor que se aplica a dicho grupo, es decir,
podemos manipular datos en brutos para obtener
resultados únicos, aunque temporales. Veamos el
agregado más simple, COUNT:

Prueba1=# SELECT COUNT(1) FROM alumnos;


count
-------
3
(1 row)
16

Prueba1=#

¿qué significa el número 1 que se encuentra como


parámetro en COUNT? Que cuente el número de
registros, si hacemos:

Prueba1=# SELECT 1 FROM alumnos;

?column?
----------
1
1
1
(3 rows)

nuestro SELECT muestra un número 1 por cada registro


seleccionado. Tanto COUNT(1) como COUNT(nom) nos dan
el mismo resultado. Aunque debemos tener mucho
cuidado con el valor NULL en los campos. Analicemos
el siguiente ejemplo:

Prueba1=# insert into alumnos values(’TUX’,’Family’,null,null);

INSERT 14284416 1

Prueba1=# SELECT COUNT(nom) FROM alumnos;

count
-------
4
(1 row)
Prueba1=# SELECT COUNT(clase) FROM alumnos;

count
-------
3
(1 row)

Prueba1=#

Agrupando los agregados

Si en vez de contar los registros de la tabla


alumnos, contamos los alumnos por clase, nada es más
fácil, bastará con agrupar nuestro agregado para las
clase con la cláusula GROUP BY. Veamos el siguiente
ejemplo:

Prueba1=# select count(1) as total,clase from alumnos group by clase;

total | clase
-------+--------
2 | 3A
1 | CP
1 |
(3 rows)

podemos eliminar los alumnos que no tienen clase con


la cláusula WHERE:

Prueba1=# select count(1) as total,clase from alumnos

Prueba1=# where clase IS NOT NULL group by clase;

total | clase
-------+--------
2 | 3A
1 | CP
(2 rows)

también podemos especificar cuales son los grupos que


nos interesan con la cláusula HAVING:

Prueba1=# select count(1) as total,clase from alumnos

Prueba1=# where clase IS NOT NULL group by clase HAVING count(1)


> 1;

total | clase
-------+--------
2 | 3A
(1 row)

Atentos a no confundir WHERE y HAVING. WHERE


especifica el cursor sobre el que queremos trabajar;
mientras que HAVING determina los grupos que
queremos. Existen otras funciones de agregado como
SUM, MAX y MIN. Analicemos el siguiente ejemplo:

select max(edad), min(edad) from alumnos group by clase ;

que regresa las edades de los alumnos, el mayor y el


menor de ellos.

Las transacciones
Las transacciones son completamente indispensables en
un SGBD ya que garantizan que el estado de los datos
siempre es coherente y dependiente de las
circunstancias. Una transacción se puede definir (de
manera simple) como una continuación de las
operaciones que efectúa completa o parcialmente.
Cuando compramos pan, efectuamos una transacción
constituida por 2 operaciones: da una orden al
panadero y éste nos da el pan. Aunque podemos
considerar una tercera operación, pagar el pan. Si
una de las operaciones no se completa, se nos olvido
el pedido, la apariencia del pan no nos gusta o
cambiamos de opinión, entonces la transacción
completa es anulada. Por lo que no es posible que
solamente una parte de la transacción se actualice,
ocasionando problemas de coherencia e integridad.
Pero lo más importante, transacción, tiene una
connotación financiera, dependiendo del SGBD las
transacciones cubren todas las manipulaciones que son
necesarias a las operaciones. Como, si requerimos
transferir un alumno antiguo de la tabla de alumnos a
la tabla egresados, deberemos insertar estos alumnos
en la nueva tabla egresados y después suprimirlos de
la tabla alumnos. No hay garantía sobre una
interrupción en el servidor, si la segunda operación
no se efectúo, nuestra base de datos se vuelve
incoherente, ya que ambas operaciones deben
efectuarse por completo.
Desde un punto de vista práctico, resolver el
problema es muy simple. Una transacción se inicia con
la sentencia BEGIN TRANSACTION, quien nos asegura que
todas las modificaciones realizadas sobre la base de
datos NO son visibles en el interior de la misma
transacción. Para que se hagan efectivas se debe
alcanzar el comando COMMIT, pero si por alguna
circunstancia se detiene o altera la secuencia de
ejecución podemos deshacer las modificaciones
efectuadas con el comando ROLLBACK.
¿Qué es lo que realmente pasa? Las sentencias de
modificación son programadas (escritas para ser
ejecutadas progresivamente) en un libreto de
transacciones. Algunas modificaciones efectuadas
sobre la base de datos hasta que la operación no pase
físicamente a escribirse en el libreto de
transacciones. Insisto en lo físico, además les diré
que hay SGBD que no sólo escriben sobre el libreto
sino que también lo hacen sobre buffers, caches, etc.
Veamos un ejemplo de una transacción:

Prueba1=# CREATE TABLE TEST(A INTEGER);

CREATE

Prueba1=# BEGIN TRANSACTION;

BEGIN
Prueba1=# INSERT INTO TEST VALUES(1);

INSERT 14284476 1

Prueba1=# SELECT * FROM TEST;

a
---
1
(1 row)

Prueba1=# ROLLBACK;

ROLLBACK

Prueba1=# SELECT * FROM TEST;

a
---
(0 rows)

Esta transacción está terminada por ROLLBACK, las


modificaciones inscritas en el libreto de
transacciones no son llevadas a la base de datos.
Debemos notar que ciertas sentencias como TRUNCATE
TABLE y DROP TABLE no son inscritas en el libreto de
transacciones por lo que es imposible hacer un
ROLLBACK de ellas. PosgreSQL inscribe la sentencia
CREATE TABLE lo que no ocurre en la mayoría de los
SGBD. Es necesario notar que cuando abrimos una
transacción, todas las operaciones de modificación
que efectuemos son inscritas en el libreto
respectivo. Si estas operaciones son voluminosas, el
libreto crece, después de regresa al tamaño original
cuando se termina la transacción; así UPDATE sobre
un tabla de 15 millones de registros no modifica el
tamaño de la base de datos.
Los registros de la tabla están ya presentes, por lo
que sólo hay que modificar valores de columnas. Sin
embargo, es muy necesario inscribir en el libreto las
operaciones efectuadas, aunque el tamaño del libreto
se incremente hasta que la transacción termine.
Si nuestra sentencia UPDATE toma 10 minutos para
ejecutarse, hay una fuerte opinión de que el ROLLBACK
como fin de transacción tomará igualmente 10 minutos,
puesto que recorre (reversa) el libreto desde la
primera operación hasta la última para anular dicha
transacción.
Notemos dos interesantes posibilidades del empleo de
libretos (que no corresponden al propósito de
PostgreSQL pero que simplifican nuestra
codificación):
• El salvavidas. Más que proteger la base de datos
todos los días, protegemos ciertos datos. El
método de protegerlos en un instante T, entonces
enviarlos al disco cada vez que se abra el archivo
de log. Tal que sea posible, en un momento de
necesidad ,tomar el respaldo T y aplicarle las
operaciones de modificación previamente
efectuadas.
• La replicación. Consiste en tener servidores
distintos en donde replicar la base de datos.

Constricciones y secuencias
Para establecer las relaciones entre tablas, debemos
establecer un esquema de base de datos adecuado,
propio y estructurado. Las constricciones mantienen a
un grupo de ellas. Regresemos a nuestra conocida
tabla alumnos. Como ejercicio queremos evitar la
inserción de un alumno que no tenga nombre, es decir,
nombre es un campo no indeterminado. Durante la
creación de la tabla, simplemente constriñimos la
columna, campo, nombre con: NOT NULL.

Prueba1=# CREATE TABLE alumnos(

Prueba1=# nom VARCHAR NOT NULL,

Prueba1=# apellidos VARCHAR,

Prueba1=# edad INTEGER,


Prueba1=# clase CHAR(2) );

CREATE

Prueba1=#

si tratamos de insertar un registro donde el campo


nombre sea NULL, el SGBD se obstina es decirnos que
no se puede!

Prueba1=# INSERT INTO alumnos VALUES(null,null,null,null);

ERROR:~ ExecAppend: Fail to add null value in not null


attribute nom

Prueba1=# INSERT INTO alumnos VALUES(’DUPONT’,null,null,null);

INSERT 22664 1

Prueba1=# UPDATE alumnos SET nom=null;

ERROR:~ ExecReplace: Fail to add null value in not null


attribute nom

Prueba1=#

pregunta: ¿el nombre de un alumno es suficiente para


identificarlo? Bien sabemos que no. Si pedimos que el
alumno MARTÍN pasa a la clase 3A, hacemos:

UPDATE alumnos SET clase=’3A’ WHERE nom=’MARTIN’;

¡Hemos incluido todos los alumnos con ese nombre en


la clase 3A! Por lo que podemos decir que un alumno
se identifica por su nombre y apellido, aunque
debemos tener cuidado porque los homónimos son raros
pero existen. Para evitar este tipo de problemas,
debemos emplear una característica propia a cada
alumno, como el número de carnet, el número del
seguro social, el número del pasaporte o el número de
cédula de identidad, y le llamamos clave primaria.
Llamamos a la clave cada vez que necesitemos filtrar
tuplas. Así, nombre alumno es una clave, lo mismo
nombre+apellido, es una convención e invoca a una
característica que hace único a un alumno o registro.
Por lo que no puede haber registros repetidos o
duplicados en cuanto a la clave.
Si nuestras claves primarias son funcionales, ellas
frecuentemenete están compuestas de varios campos, al
hacer uniones (JOIN) nos veremos obligados a escribir
interminables cláusulas WHERE, lo que preocupa en
cuanto al desempeño de nuestra base de datos. Por lo
que nuestra sugerencia es escribir claves
informáticas. Se asigna un valor, sin significado
como atributo a cada registro y que sirva de clave
primaria. Veamos varias ventajas de esto:
• Estos valores son generados automáticamente cada
vez que se inserta un nuevo registro.
• Hay riesgo de colisión, por homónimos, en una red
de computadoras.
• Una clave primaria sobre un solo campo es muy
ventajoso en la práctica cuando necesitemos
realizar uniones (JOIn).
• Una clave primaria numérica ejecutada en tiempo de
acceso no ocupa gran volumen de almacenaje.

SECUENCIAS

Para generar valores automáticos utilizamos las


secuencias. Se manejan como las tablas:

Prueba1=# CREATE SEQUENCE seq_alumnos INCREMENT 1 START


1;

CREATE

Prueba1=# select nextval(’seq_alumnos’);

nextval
---------
1
21
(1 row)
Prueba1=# select nextval(’seq_alumnos’);

nextval
---------
2
(1 row)

Prueba1=# select nextval(’seq_alumnos’);

nextval
---------
3
(1 row)

Prueba1=# select last_value from seq_alumnos;

last_value
------------
3
(1 row)

Prueba1=# select nextval(’seq_alumnos’);

nextval
---------
4
(1 row)

Prueba1=# select last_value from seq_alumnos;

last_value
------------
4
(1 row)
como podemos ver, la secuencia genera valores
numéricos sucesivos. En el proceso de creación de la
secuencia le indicamos que comience en el valor 1,
para que después le añada un uno como respuesta al
llamado de NEXTVAL().
Si regresamos a nuestra tabla alumnos, e insertamos
una columna denominada id (identificador del alumno)
como clave primaria proporcinado por nuestra
secuencia:

Prueba1=# DROP TABLE alumnos;

DROP

Prueba1=# CREATE TABLE alumnos(

Prueba1=# id INTEGER NOT NULL DEFAULT


NEXTVAL(’seq_alumnos’) PRIMARY KEY,

Prueba1=# nom VARCHAR NOT NULL,

Prueba1=# apellidos VARCHAR,

Prueba1=# edad INTEGER,

Prueba1=# clase CHAR(2) );

NOTICE: CREATE TABLE/PRIMARY KEY will create implicit


index ’alumnos_pkey’for table ’alumnos’

CREATE

Prueba1=#

En la nueva definición de la tabla alumnos tenemos un


campo que no puede contener un valor NULL, por
consiguiente si ocurre ningún índice se proporcionará
en el momento de la inserción del nuevo registro; en
caso contrario se suministra la sucesión
seq_alumnos. El SGBDR responde que ha creado
automáticamente un índice para manejar esta clave
primaria. Ahora, veamos lo que da:

Prueba1=# INSERT INTO alumnos(nom,apellidos) VALUES


(’MARTIN’,’Philippe’);

INSERT 22816 1

Prueba1=# SELECT * FROM alumnos;

id | nom | apellidos | edad | clase


----+--------+----------+-----+--------
5 | MARTIN | Philippe | |
(1 row)

Prueba1=# INSERT INTO alumnos(id,nom,apellidos) VALUES


(5,’TUX’,’Family’);

ERROR: Cannot insert a duplicate key into unique index


alumnos_pkey

Prueba1=# INSERT INTO alumnos(nom,apellidos) VALUES


(’TUX’,’Family’);

INSERT 22818 1

Prueba1=# SELECT * FROM alumnos;

id | nom | apellidos | edad | clase


----+--------+----------+-----+--------
5 | MARTIN | Philippe | |
6 | TUX | Family | |
(2 rows)

Prueba1=#
si creamos ahora una nueva tabla mensajes que
contiene los mensajes enviados a los padres de los
alumnus. Esta tabla es muy simple, contiene la fecha
y la hora del mensaje, el texto y el identificador
del alumno. El identificador del alumno en la tabla
mensajes se denomina clave foránea, ya que es la
clave primaria de otra tabla. He aquí un ejemplo:

Prueba1=# CREATE TABLE messedads (

Prueba1=# id INTEGER NOT NULL REFERENCES alumnos,

Prueba1=# texte TEXT,

Prueba1=# dateheure DATETIME NOT NULL DEFAULT now());

NOTICE:~ CREATE TABLE will create implicit trigger(s) for


FOREIGN KEY check(s)

CREATE

Prueba1=#

Con la constricción de columna REFERENCE indicamos


que el valor del campo es una clave primaria de la
tabla alumnos. Debmeos remarcar que algunas opciones
permiten definir el comportamiento de la base de
datos en caso de supresión de un alumno (los mensajes
son suprimidos porque pasa a NULL o porque la
supresión es cancelada debido a la existencia de
mensajes). El tipo TEXTO es el equivalente de un tipo
VARCHAR, pero por normas SQL permite contener más
caracteres. Notemos el tipo DATETIME que es fecha y
hora, y es obtenido del valor retornado de la función
NOW().
Si queremos agregar el número INSEE del alumno,
sabemos que este número es único, pero no quisimos
usarlo como clave primaria para ser capaz insertarlo
en nuestra tabla de alumnos de cuál no sepamos el
número INSEE. Uno puede agregar esta columna no
obstante con el constreñimiento UNIQUE:
Prueba1=# DROP TABLE alumnos;

NOTICE:~ DROP TABLE implicitly drops referential integrity


trigger from table "messedads"

DROP

Prueba1=# CREATE TABLE alumnos(

Prueba1=# id INTEGER NOT NULL DEFAULT


NEXTVAL(’seq_alumnos’) PRIMARY KEY,

Prueba1=# nom VARCHAR NOT NULL,

Prueba1=# apellidos VARCHAR,

Prueba1=# edad INTEGER,

Prueba1=# clase CHAR(2),

Prueba1=# insee CHAR(15) NULL UNIQUE );

NOTICE: CREATE TABLE/PRIMARY KEY will create implicit


index ’alumnos_pkey’ for table ’alumnos’

NOTICE:CREATE TABLE/UNIQUE will create implicit index


’alumnos_insee_key’ for table ’alumnos’

CREATE

Prueba1=#

Así que nuestro SGBD nos permitirá la inserción


algunos registros sin informar el campo INSEE. Sin
embargo, si esto es informado, el SGBD verificará que
no haya sido usado ya.

Constriñendo las tablas

Las constricciones que evocamos pueden ser sobre


múltiples columnas. Por ejemplo, cada vagón de
ferrocarril tiene asientos numerados de 1 a 90, la
tripleta (numéro de ferrocarril, numéro de carro,
numéro de asiento) lo hace único.
Las constricciones de la tabla son adicionadas
después de la declaración de las columnas en el
proceso de creación de la tabla:

Prueba1=# CREATE TABLE reservations(

Prueba1=# no_train INTEGER NOT NULL,

Prueba1=# no_voiture INTEGER NOT NULL,

Prueba1=# no_siege INTEGER NOT NULL,

Prueba1=# UNIQUE (no_train,no_voiture,no_siege));

NOTICE:~ CREATE TABLE/UNIQUE will create implicit index


’reservations_no_train_key’ for table ’reservations’

CREATE

Prueba1=#

Como sabemos, el SGBD crea un índice por gestionar la


constricción. Por consiguiente debemos estar muy
atentos al orden en que enumeramos nuestras columnas
en la constricción UNIQUE para mejorar el desempeño
de nuestra aplicación.
También podemos constreñir una tabla para definir una
clave foránea hacia una tabla cuya clave primaria
está compuesta de varias columnas. Otras opciones de
constricción están igualmente disponibles, por lo que
invitamos a los lectores a consultar su manual de
referencia para tener la lista exhaustiva de ellas.
No obstante, algunas restricciones de columnas y
tablas (NOT NULL, UNIQUE, REFERENCES, PRIMARY KEY)
son por lo general suficiente para trabajar de bases
de datos pequeñas. Llamo una base de datos pequeña de
esquemas de aproximadamente un máximo de treinta
tablas donde el volumén de la más grande está por
debajo a 250 000 líneas. El sitio de LinuxFrench por
ejemplo no utiliza una "base de datos pequeña".

Procedimientos almacenados (funciones)


En lugar de realizar todas los requerimientos SQL
desde la aplicación del cliente, es preferible
exportarlos al SGBD mismo, este es el papel de los
procedimientos almacenados. PostgreSQL permite
programar estos procedimientos en diferentes
lenguajes, por lo que hablaremos de escribirlos en
pl/pgSQL. ¿Por qué está elección? aunque este
lenguaje es muy similar al pl/SQL de Oracle, es
diferente, más parecido a transact-SQL de SYBASE.
Debemos comenzar por activar el lenguaje desde
nuestra base de datos. Si utilizamos Linux Mandrake
tiene las siguientes instrucciones:

[root@localhost src]# su - postgres

[root@localhost src]# export PGLIB=/usr/lib/

[root@localhost src]# createlang plpgsql martin

[root@localhost src]#

«Prueba1» es el nombre de nuestra base de datos. En


PostgreSQL, los procedimientos almacenados son de
hecho las funciones que podemos utilizar en nuestros
comandos SELECT como cualquier otra función. Si
utiliza otra versión, investigue el repertorio que se
encuentra en plpgsql.so, inicialice PGLIB de forma
que apunte al repertorio y cree el lenguaje con
createlang.
Los procedimientos almacenados tiene diversos
intereses:
• Lo primero es tener una entidad funcional. Qué
es decir, tener un segmento de código con
significación física sobre la base de datos. Por
ejemplo, si lo que queremos en eliminar un élève
de la base de datos, se podrían requerir de
otras operaciones que un simple DELETE FROM
alumnos, como la necesidad de también eliminar
los mensajes de los padres, etc. Podemos
reagrupar todas estas operaciones en un único
procedimiento supprimer_eleve().
• Segundo, debemos factorizar el código, es decir,
tener un código único, más que muchas copias del
mismo dispersas, por ahí, en la aplicación. Por
ejemplo, si requerimos modificar la estructura
de una tabla y evitar cantidad de problemas,
será suficiente actualizar los procedimientos
almacenados respectivos en lugar de auditar la
aplicación completa.
• En terminos de razón de desempeño, los
procedimientos almacenados aportan mucho. En
efecto, cuando es necesario realizar operaciones
sucesivas, que producen caros viajes, ida y
vuelta, en las redes en el término de actuación
entre el SGBDR y la aplicación. Mientras que con
un simple llamado al procedimiento desde la
aplicación, todas las operaciones son efectuadas
en el mismo SGBDR.
• Todavía en terminos de desempeño, economiza el
analises lexicográfico y sintáctico del código
requerido. Estas cosas han sido hehcas una sola
vez. Ciertos SGBD (no es el caso de pgSQL)
efectuan una compilación completa del
procedimiento, con un precalculo de los planes
de ejecución.
¡Suficiente teoría! Seamos un poco más concretos,
veamos la siguiente tabla simple con algunos datos:

Prueba1=# create table comptes(no integer primary key, solde


decimal(10,2));

...

Prueba1=# select * from comptes;

no | solde
----+---------
1 | 100.00
2 | 1000.00
3 | 1500.00
4 | 10.00
creemos una función para efectuar una transferencia
de una cuenta hacia otra. Por facilidad, es
preferible grabar el código SQL de creación del
procedimiento en un archivo de texto:

create function virement(integer,integer,decimal) returns integer

AS ’

DECLARE crediteur ALIAS FOR $1;

DECLARE debiteur ALIAS FOR $2;

DECLARE montant ALIAS FOR $3;

BEGIN

UPDATE comptes SET solde=solde+montant WHERE no=crediteur;

UPDATE comptes SET solde=solde-montant WHERE no=debiteur;

return 0;

END;

’ LANGUAGE ’plpgsql’;

Veamos la estructure general de creación de un


procedimiento (función):

CREATE FUNCTION nom_función(paramètres) RETURNS tipo AS


’codigo de la funcion’ LANGAGE ’lenguaje’ ;

en nuestro caso, nuestra función se llama virement,


admite 2 parámetros numéricos enteros y un decimal.
Regresa un valor entero (sin significado) y está
programada en pl/pgSQL.
Entonces, damos un nombre a nuestros parámetros, lo
que será más explicito que 1 2 etc. Después,
_

comenzamos el bloque de instrucciones PL/pgSQL, donde


hacemos nuestras dos actualizaciones sucesivas con el
objetivo de efectuar:

Prueba1=# select * from comptes;

no | solde
----+---------
2 | 1000.00
3 | 1500.00
4 | 10.00
1 | 100.00
(4 rows)

Prueba1=# select virement(1,2,100);

virement
----------
0
(1 row)

Prueba1=# select * from comptes;

no | solde
----+---------
3 | 1500.00
4 | 10.00
1 | 200.00
2 | 900.00
(4 rows)
de seguro será un poco riesgoso, por lo que
deberiamos hacer:
BEGIN TRANSACTION; select virement(1,2,100); COMMIT;

Nuestro procedimiento debería verificar que la cuenta


tiene saldo antes de efectuar la transferencia.
Veamos la nueva versión:

drop function virement(integer, integer, decimal);

create function virement(integer,integer,decimal) returns integer

AS ’

DECLARE crediteur ALIAS FOR $1;

DECLARE debiteur ALIAS FOR $2;

DECLARE montant ALIAS FOR $3;

DECLARE ancien_solde DECIMAL;

BEGIN

SELECT solde INTO ancien_solde FROM comptes WHERE


no=debiteur;

IF (ancien_solde > montant) THEN

UPDATE comptes SET solde=solde+montant WHERE no=crediteur;

UPDATE comptes SET solde=solde-montant WHERE no=debiteur;

return 0;

END IF;

return -1;

END;
’ LANGUAGE ’plpgsql’;

hemos declarado una nueva variable ancien_solde a la


cual le afectamos un valor con un comando SELECT
INTO. En este caso, la cláusula INTO asigna una
variable y no una tabla a crear. Veamos la versión
terminada del procedimiento que verifica la
existencia de las cuentas:

drop function virement(integer, integer, decimal);

create function virement(integer,integer,decimal) returns integer

AS ’

DECLARE crediteur ALIAS FOR $1;

DECLARE debiteur ALIAS FOR $2;

DECLARE montant ALIAS FOR $3;

DECLARE ancien_solde DECIMAL;

DECLARE test INTEGER;

BEGIN

SELECT solde INTO ancien_solde FROM comptes WHERE


no=debiteur;

IF NOT FOUND THEN

RAISE EXCEPTION ’’Compte no % Inconnu’’,debiteur;

END IF;

IF (ancien_solde > montant) THEN

SELECT count(1) into test FROM comptes WHERE


no=crediteur;
IF (test <> 1) THEN

RAISE EXCEPTION ’’Compte no % Inconnu’’,crediteur;

END IF;

UPDATE comptes SET solde=solde+montant WHERE no=crediteur;

UPDATE comptes SET solde=solde-montant WHERE no=debiteur;

return 0;

END IF;

return 1;

END;

’ LANGUAGE ’plpgsql’;

el código merece algunas explicaciones. La cláusula


IF NOT FOUND sera verificada si la instrucción
precedente no encuentra un registro a manipular. En
el mismo orden de ideas, SELECT count(1) INTO test
nos sirve para verificar que exite una sola cuenta
que tiene ese numéro. Esta situación es inútil a
partir de la version 7.1 de postgreSQL, puesto que
podemos emplear la instrucción GET DIAGNOSTICS test =
ROW_COUNT para conocer directamente el número de
registros manipulados por la última instruction SQL.
La instrucción RAISE EXCEPTION administra los mendaje
de error. Note que las cadenas de caracteres están
enmarcadas entre dobles apostrofos ”. En efecto, el
código de nuestro mismo procedimiento es una cadena
de caracteres ( CREATE FUNCTION nom() RETURNS tipo AS
’code’...), es necesario utilizar escape en los
delimitadores de cadenas en el procedimiento. Podemos
utilizar \’ como escape.
Igualmente es muy importante tener RAISE EXCEPTION
para administrar las excepciones durante la ejecución
de nuestro código. Si no encontramos una transacción
(siempre existe el riesgo de no encontrar la cuenta o
tener saldo o excepciones), la operación será
anulada.
Total, también es posible llamara a un procedimiento
almacenado en un orden dado. Si requerimos que todas
las cuentas hagan una transferencia de un dólar sobre
la cuenta numéro 1, entonces hacemos:

Prueba1=# select * from comptes;

no | solde
----+---------
3 | 1500.00
4 | 10.00
2 | 797.00
1 | 296.00
(4 rows)

Prueba1=# BEGIN TRANSACTION; select virement(1,no,1) FROM


COMPTES WHERE no <>1; COMMIT;

BEGIN
virement
----------
0
0
0
(3 rows)
COMMIT

Prueba1=# select * from comptes;

no | solde
----+---------
3 | 1499.00
4 | 9.00
2 | 796.00
1 | 299.00
(4 rows)

Prueba1=#

Triggers (disparadores)
Los triggers son un tipo de constricción, un elemento
fundamental en la estructura de las bases de datos.
Donde una constricción permite definir una regla
algebraica, el trigger permite definir una regla
algoritmica. Muy útil desde el punto de vista
funcional.

¿Qué es un trigger?

El trigger es una función que afecta a una tabla y


controla ciertas operaciones sobre ella.
Veamos dos ejemplos que demeustrasn la utilidad de
los triggers:
• Constricción lógica. Con REFERENCES definimos
las constricciones sobre claves foráneas.
También, podemos definir constricciones de tipo
“de este tipo o de este otro tipo”. Como tener
dos campos que son claves foráneas, pero para un
registro no es posible tener los dos campos
asignados. Por tanto, nos es muy útil tener una
función de control automática que a la vez que
escribe un valor en un campo, verifique que se
cumpla cierta condición para el nuevo valor.
• Utilidad funcional. Una de nuestras tablas
contiene un campo texto y requerimos grabar una
traza de las sucesivas versiones del texto. Más
que manejarla dentro de nuestra aplicación (con
el riesgo del mantenimiento evolutivo, al que un
desarrollador se compromete para conservar la
integridad de dicha gestión en una nueva porción
de código que permita modificar el texto), es
más onfiable y simple definir un trigger,
controlador sobre UPDATE, que irá archivando la
versión anterior del texto antes de efectuar la
actualización.
Un trigger es un controlador que se verifica antes de
una instrucción de tipo INSERT, DELETE o UPDATE. De
hecho, puede ser llamado para cada uno de los
registros sujetos a la instrucción o una sola vez.

¿Cómo hacer un trigger ?

Lo primero es programar un procedimiento almacenado.


Este procedimiento (o función) constituye el trigger
propiamente. Esta función no admite argumentos y
regresa el tipo OPAQUE (tipo indeterminado).
Cuando una función es llamada por un trigger, hereda
automáticamente, en tiempo de ejecución, un cierto
número de variables particularmente útiles, como los
citados a continuación:
• NEW: el registro que se genera después de la
ejecución de la instrucción que invoca el
trigger.
• OLD: el estado del regsitro antes de la
ejecución de la instrucción que invoca el
trigger.
Veamos un pequeño ejemplo. Crearemos dos tablas, una
para artículos escritos y la otra para las versiones
sucesivas de los artículos.

Prueba1=# create table article(id integer not null primary key,


date_modif

date not null default now(), texte text, status varchar);

NOTICE:~ CREATE TABLE/PRIMARY KEY will create implicit


index ’article_pkey’ for table ’article’

CREATE

Prueba1=# create table archive(id integer, date_modif date not null,


texte text);

CREATE

Prueba1=#
programamos una función que será invocada durante la
modificación de un artículo con el objetivo de
conservar la versión anterior de dicho artículo:

DROP FUNCTION arch_art();

CREATE FUNCTION arch_art() RETURNS OPAQUE AS ’

BEGIN

IF NEW.texte != OLD.texte THEN

INSERT INTO archive(id, date_modif, texte)

VALUES (OLD.id,OLD.date_modif,OLD.texte);

END IF;

RETURN NEW;

END;

’ LANGUAGE ’plpgsql’;

La última línea (RETURN NEW ), retorna el registro


que deberá ser escrito en la base de datos.
En nuestro caso, permanece sin cambios con relación
al comando UPDATE original.
Resaltamos que el trigger será llamado por cada
comando UPDATE, debido a que es necesario verificar
que el texto del artículo ha sido modificado antes de
grabarlo.
Luego de programar el trigger, le indicamos a la base
de datos la tabla, las operaciones y el procedimiento
almacenado respectivos:

DROP TRIGGER trg_arch_art ON article;


CREATE TRIGGER trg_arch_art BEFORE DELETE OR UPDATE ON
article

FORE ACH ROW EXECUTE PROCEDURE arch_art();

Verificamos que el trigger trabaja correctamente:

Prueba1=# insert into article(id,texte) values(1,’Ceci est le premier


article original’);

INSERT 40942 1

Prueba1=# select * from article;

id | date_modif | texte | status


----+------------+--------------------------------------+--------
1 | 2001-11-19 | Ceci est le premier article original |
(1 row)

Prueba1=# select * from archive;

id | date_modif | texte
----+------------+-------
(0 rows)

Prueba1=# update article set texte=’Ceci est le premier article version


2’, date_modif=now();

UPDATE 1

Prueba1=# select * from article;

id | date_modif | texte | status


----+------------+---------------------------------------+--------
1 | 2001-11-19 | Ceci est le premier article version 2 |
(1 row)

Prueba1=# select * from archive;

id | date_modif | texte
----+------------+--------------------------------------
1 | 2001-11-19 | Ceci est le premier article original
(1 row)

Prueba1=#

ahora modificaremos nuestro trigger con el propósito


de rechazar las modificaciones si el campo status
indica ’publicado’ :

DROP FUNCTION arch_art();

CREATE FUNCTION arch_art() RETURNS OPAQUE AS ’

BEGIN

IF NEW.texte != OLD.texte THEN

IF OLD.status = ’’publicado’’ THEN

RAISE EXCEPTION ’’Article % déjà publié’’,NEW.id;

END IF;

INSERT INTO archive(id, date_modif, texte)

VALUES (OLD.id,OLD.date_modif,OLD.texte);

END IF;

RETURN NEW;

END;

’ LANGUAGE ’plpgsql’;

DROP TRIGGER trg_arch_art ON article;


CREATE TRIGGER trg_arch_art BEFORE DELETE OR UPDATE ON
article

FOR EACH ROW EXECUTE PROCEDURE arch_art();

Veamos el resultado:

Prueba1=# select * from article;

id | date_modif | texte | status


----+------------+---------------------------------------+--------
1 | 2001-11-19 | Ceci est le premier article version 2 |
(1 row)

Prueba1=# select * from archive;

id | date_modif | texte
----+------------+--------------------------------------
1 | 2001-11-19 | Ceci est le premier article original
(1 row)

Prueba1=# update article set status=’publié’ where id=1;

UPDATE 1

Prueba1=# select * from article;

id | date_modif | texte | status


----+------------+---------------------------------------+--------
1 | 2001-11-19 | Ceci est le premier article version 2 | publié
(1 row)

Prueba1=# select * from archive;

id | date_modif | texte
----+------------+--------------------------------------
1 | 2001-11-19 | Ceci est le premier article original
(1 row)

Prueba1=# update article set texte=’Ceci est une tentative de


modification’, date_modif=now();

ERROR: Article 1 déjà publié


Prueba1=#

Como podemos ver, los triggers son muy practicos para


agilizar la aplicación cliente de la gestión de las
reglas de flujo de trabajo, por ejemplo. También son
muy aconsejables para generar reglas de seguridad.

Optimizaciones y desempeño
El desempeño es una problemática invocada por las
bases de datos. Hablamos de entonar (ajustar los
parámetros para afinar el desempeño). Es necesario
medir la magnitud en los cambios y comprender cómo
funciona los engranes de un SGBDR antes de poder
tratar los problemas de desempeño.

Niveles de optimización

Los problemas de desempeño no son un problema global


en un sistema de información. Debe ser tratado nivel
por nivel.
• Nivel conceptual. Consiste en la correcta
concepción del esquema físico de la base de
datos. Crear los índices adecuados, elegir
correctamente los tipos de los datos, etc.
• Nivel de aplicación. Trata sobre todo de
escribir correctamente los requerimientos.
• Nivel de servidor. Primero el material, luego la
configuration y por último la parametrización
del servidor de base de datos.
Para cada uno de estos puntos, es necesario tener una
buena comprensión del funcionamiento interno de los
SGBD, con el objetivo de elegir las opciones
adecuadas.

Nivel conceptual

La pregunta principal trata con los índices. Bien,


los campos declarados deben ser una clave primaria
para que se vean automaticamente indexadoss. Pero, de
manera general, todos los campos son susceptibles de
ser utilizados como clave de acceso (total, todo está
relacionado con ?) a ser indexado.
Por ejemplo, en nuestra tabla alumnos, tenemos una
clave informática (id) sobre la cual el SGBD
automaticamente coloca un índice. Pero sería bueno
crear un índice sobre los nombreos y apellidos y
"número_insee". Ahora, es necesario hacernos la
pregunta de saber si la creación de un índice
compuesto de varios campos es discriminante o no.
Para responderla, debemos comprender muy bien el
funcionamiento de un índice. En una tabla, cada
registro está dotado de un objeto identificador - OID
- quien es una clase de dirección. El SGBDR utiliza
internamente este OID para construir las tuplas. Un
OID es único en la constitución de la base de datos.
imaginemos una tabla de 8 registros, muy simple,
conteniendo enteros del 1 al 8:
OID valor
222 1
223 2
224 3
225 4
226 5
227 6
228 7
229 8
Si la tabla no tiene índice, y cuando hacemos una
requisición a través de una cláusula WHERE valor=5,
el SGBDR examina cada registro de la tabla, y envia
el OID de cada tupla donde el campo valor es igual a
5. estás por decirme que es suficiente examinar 5
registros para encontrar la tupla que nos interesa.
Pues no! El SGBDR deberá examinar los 8 registros
para verficar que no existe otra tupla con valor=5.
esto se debe a que el empleo de una simple
constricción UNIQUE durante la creación de la tabla
puede influir sobre el desempeño!
Cuando creamos un índice sobre un valor, un árbol
equlibrado es creado, la organización
esquemáticamente se puede representar como:
Nodo 1 Nodo 2 Nodo 3 Nodo 4
1 (222)
2(223) 3 (224)
4 (225)
5 (226)
6 (227)
7(228)
8 (229)
Entonces, si buscamos el valor 6 por ejemplo,
accedemos al primer nodo. El valor de este nodo es 5,
m{as bajo que la clave buscada. Pasamos al nodo 2,
tomando la rama baja. Su valor es 7, pasamos al rama
superior del nodo, y encontramos el nodo 6, que nos
indica que el registro que nos interesa se ubica en
OID 227. hemos necesitado tres compraciones en lugar
de ocho!. Siempre que haya una organización de
números secuenciales el número de comparaciones crece
linealmente en función del número de registros en la
tabla, en un BTree, el número aumenta en forma
logaritmica.
Cuando creamos un índice compuesto, las claves del
BTree son simplemente la concatenación de los campos
que componen el índece. Por consiguiente, si nuestro
índice está compuesto por (nombre,apellido), todavía
es posible navegar por el mismo árbol para buscar el
nombre. El índice encontrará rápidamente a todos los
alumnos cuyo nombre es "Dupont". Por el contrario, si
nuestra búsqueda se efectúa únicamente por el
apellido, el índice no es útil: ¿cómo podemos
efectuar la comparación de las claves? Como por
ejemplo, ¿Dupont|Jean es superior o inferior a
Albert?

Escribiendo requerimientoss, tablas directrices y estadísticas

Veamos el siguiente ejemplo, seleccionamos el


Apellido de los alumnos, por ejemplo, en las dos
tablas alumnos y clases:

Prueba1=# select * from alumnos;

id | nom | apellidos | edad | clase | insee


----+---------+-----------+-----+----+-------
7 | Alpha | A | 15 | 3A |
8 | Bravo | B | 15 | 3A |
9 | Delta | D | 15 | 3B |
10 | Epsilon | E | 16 | 3B |
11 | Gamma | E | 16 | 3A |
12 | Mu | M | 16 | 3A |
13 | Nu | N | 16 | 3B |
14 | Omega | O | 16 | 3B |
15 | Chi | Q | 16 | 3A |
(9 rows)
Prueba1=# select * from clases;

clase
--------
3B
3A
(2 rows)

Prueba1=#

ahora, empleamos la directiva EXPLAIN para tener una


previsualización del funcionamiento interno del
SGBDR:

Prueba1=# explain select * from alumnos,clases where


alumnos.clase=clases.clase;

NOTICE: QUERY PLAN:

Nested Loop (cost=0.00..32.50 rows=10 width=68)

-> Seq Scan on alumnos (cost=0.00..0.00 rows=1 width=56)

-> Seq Scan on clases (cost=0.00..20.00 rows=1000


width=12)

EXPLAIN

Prueba1=#

PostgreSQL indica que efectuará un barrido


secuencial, que directamente examinará cada registro,
uno después de otro, sobre las dos tablas. No tiene
la opción de hecho puesto que no creé el menor
índice. La tabla directriz seleccionada es alumnos.
Por lo que comenzará a examinar la tabla alumnos, y
para cada uno de los registros de esta tabla,
efectuará un barrido sobre la tabla clase para
obtener la unión (join) requerida.
PostgreSQL hace una estimación de «costo» de cada
operación, para seleccionar la mejor estrategia. La
estrategia seleccionada se denomina plan de
ejecución.
Podemos ver aquí que postgreSQL estima que el barrido
sobre la tabla alumnos es de costo nulo, mientras que
el hacerlo sobre la tabla clase costará entre 0 y 20
(es un índice, sin unidad). Para hacer esta
estimación de costo, pgSQL se basa en el tipo de
operaciones a efectuar (comparar un entero, una
cadena de caracteres, etc.) así como en el número de
líneas que contiene cada tabla. Podemos ver que pgSQL
estima que la tabla clases se comportará como 1000
líneas, mientras que la tabla alumnos se comporta
como una sola línea.
No nos sorprende que haya seleccionado efectuar un
barrido de la tabla clases para cada línea de la
tabla alumnos puesto que esto último incluye una sola
línea... pero el problema es que estas ¡cifras son
completemente falsas!. Nuestra tabla clases incluye 2
lineas y alumnos 9 lineas. ¿Qué pasas realmente?
pgSQL utiliza las estadísticas para hacer sus
evaluaciones. Es lo que llamamos optimización
estadística. Y es inispensable que estas estadísticas
estén actualizadas para que la opmización
seleccionada sea las más eficaz. Con el comando
VACUUM (o con el utilitario vacuumdb en linea de
comando, pgSQL examinará el estado de cada tabla con
el propósito de medir sus estadísticas de
actualización:

Prueba1=# vacuum;

VACUUM

Prueba1=# explain select * from alumnos,clases where


alumnos.clase=clases.clase;

NOTICE: QUERY PLAN:

Merge Join (cost=2.26..2.40 rows=2 width=68)

-> Sort (cost=1.23..1.23 rows=9 width=56)

-> Seq Scan on alumnos (cost=0.00..1.09 rows=9 width=56)

-> Sort (cost=1.03..1.03 rows=2 width=12)

-> Seq Scan on clases (cost=0.00..1.02 rows=2 width=12)


EXPLAIN

Inmediatemente vemos que para el mismo requerimiento,


el plan de ejecución cambia. De hecho, el número de
líneas estimado en las tablas es correcto. Es
necesario pensar acerca de ejecutar VACUUM para
después efectuar masivas actualizaciones sobre la
base de datos.
Si creamos un índice sobre la tabla clases (creamos
el índice idx1 en alumnos(clase)) seremos capaces de
que el plan de ejecución no cambie. ¿Por qué? Parece
que la tabla no contiene que algunas lineas, por lo
que es más costoso utilizar un índice que efectuar un
barrido secuencial.
Procedamos a llenar nuestra tabla alumnos:

Prueba1=# insert into alumnos(nom,apellidos,edad,clase)

(select nom,apellidos,edad,clase from alumnos);

INSERT 0 9

Prueba1=# insert into alumnos(nom,apellidos,edad,clase)

(select nom,apellidos,edad,clase from alumnos);

INSERT 0 18

...

Prueba1=# insert into alumnos(nom,apellidos,edad,clase)

(select nom,apellidos,edad,clase from alumnos);

INSERT 0 294912

Prueba1=# insert into alumnos(nom,apellidos,edad,clase)

(select nom,apellidos,edad,clase from alumnos);


INSERT 0 589824
Tenemos una tabla con más de un millón de lineas.
Veamos que pasa sin índice y luego con un índice:

Prueba1=# vacuum;

VACUUM

Prueba1=# explain select * from alumnos,clases where


alumnos.clase=clases.clase;

NOTICE: QUERY PLAN:

Nested Loop (cost=0.00..74731.18 rows=23593 width=68)

-> Seq Scan on clases (cost=0.00..1.02 rows=2 width=12)

-> Seq Scan on alumnos (cost=0.00..22619.48 rows=1179648


width=56)

EXPLAIN

Prueba1=# create index idx1 on alumnos(clase);

CREATE

Prueba1=# explain select * from alumnos,clases where


alumnos.clase=clases.clase;

NOTICE: QUERY PLAN:

Nested Loop (cost=0.00..61562.29 rows=23593 width=68)

-> Seq Scan on clases (cost=0.00..1.02 rows=2 width=12)

-> Index Scan using idx1 on alumnos (cost=0.00..30633.18


rows=11796 width=56)

EXPLAIN

Prueba1=#

Con un millón de lineas, postgreSQL prefiere barrer


el índice (¡entendido!). ahora utiliza la tabla
clases como directriz. Finalmente, una cosa es más
que evidente, los SGBDR no utilizan un sólo índice
por tabla. Si la tabla incluye más índices en una
consulta, el optimizador estadístico seleccionará el
que le parece más apropiado. Por ejemplo, en caso de
una cláusula WHERE del tipo WHERE edad=15 AND
nombre_freres > 3 donde ambas campos son índices, es
probable que el optimizador estadístico optará por
edad, debido a que considera que una prueba de
igualdad recupera menos lineas que una prueba de
comparación. Lo que no necesariamente es
discriminante, pero debemos recordar que investigará
porque una consulta es más compleja de ejecutar.

Requerimientos

Una pregunta: ¿Qué requerimientos debo suministrar a


un servidor pgSQL que hará xxxx?. ¡depende!
Si, depende del volumen de las tablas, de la
concurrencia de acceso, del número de clientes (o
instancias de aplicaciones) conectados
simultaneamente, del tipo de consultas y de las
constricciones de disponibilidad.
En lo que respecta a la RAM: entre más tenga es
mejor! por lo general, entre 4 y 8 Mb de RAM para una
conexión activa.
En lo que respecta al disco duro. Lo mejor es SCSI.
Consume menos recursos de CPU que IDE, porque el
acceso puede ser paralelo sobre más periféricos,
mientras que en IDE son en serie. Podríamos tener dos
discos SCSI, uno para las base de datos y el otro
para el sistema (/tmp /var etc.). Si eres afortunado,
o si tienes fuertes exigencias sobre tu SGBDR,
utiliza 2 cadenas SCSI. Una consagrada al sistema, y
una segunda en RAID-5. Con esto no sólo serás capaz
de tener una alta disponibilidad en cuanto s tu
sistema de discos, sino igualmente permitir el
paralelismo en los accesos concurrentes.
La configuración del servidor

PostgreSQL está dotado de parametrizaciones, como


cualquier otro SGBDR. No los revisaremos aquí.
Primero no he experimentado lo suficiente con dichos
ellos como para poder dar consejos. Le sugiero que
rReúna toda la documentación posible, en particular
el Manual del administrador.
Debo señalar cuatro parámetros que me parecen
primordiales en el desempeño del servidor:
• SHARED_BUFFERS que especifica el número de
páginas de 8 Kb que serán utilizadas como buffer
por postgreSQL.
• SORT_MEM que especifica la memoria que estará
disponible para que el optimizador pueda
efectuar su tarea interna. Cuando una operación
no pueda ejecutarse en memoria, se crearan
algunos archivos temporales.
• WAL_FILES indica el número de archviso que
PostgreSQL puede crear.
• WAL_BUFFERS número de buffers utilizados para el
logging.
Debo decir que si tenemos memoria suficiente, los dos
primeros parámetros no son necesarios.

Los usuarios y sus derechos: CREATE


GROUP, GRANT.
Es necesario introducir elementos de seguridad en la
base de datos. Así, si un hacker explota una falla de
nuestro servidor web, no podrá destruir nuestra base
de datos ni podrá consultar o modificar los datos
confidenciales que contenga.
Debemos decir que postgreSQL está un poco más allá en
lo que concierne los derechos de acceso sobre una
base de datos. No obstante, los principales
utilitarios están presentes.
Usuarios
Los SGBDR utilizan los conceptos de DBA y DBO.
El DBA es el administrador de la base de datos. Es el
que administra el servidor SGBDR. Es una clase de
super usuario. Con postgreSQL, por lo general se
identifica como el usuario postgres. Cuando estamos
conectados sobre Linux en la cuenta postgres, podemos
crear y eliminar las bases de datos o a los usuarios.
El DBO es el propietario de la base de datos. Puede
creae nuevas tablas, eliminarlas, generar los
derechos de los usuarios sobre cierta base de datos,
como escribir, consultar y/o eliminar datos, etc.
En un servidor, hay un DBA y muchos DBO para lo que
el servidor contiene sus bases de datos. El DBO es
quien crea la base de datos.
Hemos distinguido entre usuario y base de datos. En
efecto PostgreSQL como en Oracle se puede prestar a
confusión estos términos.
Por ejemplo, hemos creado una base de datos
linuxfrench, un usuario linuxfrench_dbo quien opera
como el administrador de la base de datos y un
usuario linuxfrench_user quien será el usuario para
la aplicación (por ejemplo, por ejemplo para los
scripts PHP de un servidor web):

[localhost]# su - postgres

[localhost]# createdb linuxfrench

CREATE DATABASE

[localhost]# createuser -W linuxfrench_dbo

Shall the new user be allowed to create databases? (y/n) n

Shall the new user be allowed to create more new users?


(y/n) y

Password:

CREATE USER

[localhost]# createuser linuxfrench_user

Shall the new user be allowed to create databases? (y/n) n


Shall the new user be allowed to create more new users?
(y/n) n

CREATE USER

[localhost]#

el DBO creará una tabla:

linuxfrench=# create table toto(a integer);

CREATE

linuxfrench=# insert into toto values (1);

INSERT 1475451 1

linuxfrench=#

finalmente, nos conectarmos como linuxfrench_user:

linuxfrench=# \q

[localhost]# psql -U linuxfrench_user -d linuxfrench

Welcome to psql, the PostgreSQL interactive terminal.

Type: \copyright for distribution terms

\h for help with SQL commands

\? for help on internal slash commands

\g or terminate with semicolon to execute query

\q to quit

linuxfrench=> select * from toto;

ERROR: toto: Permission denied.

linuxfrench=>
Nuestro usuario linuxfrench_user no puede acceder a
la tabla toto. La tabla ha sido creada por el usuario
linuxfrench_dbo, por defecto sólo este último usuario
tiene derecho de manipular la tabla. Veremos como
añadirle derechos al usuario linuxfrench_user.

GRANT y REVOKE

[localhost]# psql -U linuxfrench_dbo -d linuxfrench

Welcome to psql, the PostgreSQL interactive terminal.

Type: \copyright for distribution terms

\h for help with SQL commands

\? for help on internal slash commands

\g or terminate with semicolon to execute query

\q to quit

linuxfrench=# GRANT select ON toto TO linuxfrench_user;

CHANGE

linuxfrench=# \q

[localhost]# psql -U linuxfrench_user -d linuxfrench

Welcome to psql, the PostgreSQL interactive terminal.

Type: \copyright for distribution terms

\h for help with SQL commands

\? for help on internal slash commands

\g or terminate with semicolon to execute query

\q to quit

linuxfrench=> select * from toto;

a
---
1
(1 row)

linuxfrench=> insert into toto values(2);

ERROR: toto: Permission denied.

linuxfrench=>

debemos firmarnos como dbo para otorgar al usuario


linuxfrench_user el derecho de accesar a la tabla
toto. Lo cual hacemos con el comando GRANT. Aunque
debemos decir que este usuario puede efectuar SELECT
sobre la tabla, pero le es imposible hacer un INSERT.
De hecho, con el comando GRANT, asignamos los
derechos para cada comando SQL : SELECT, INSERT,
UPDATE, DELETE.
Así que, si nuestra aplicación es un sitio web, es
recomendable no otorgarle al usuario que utilizará
sus scripts PHP el derecho de efectuar SELECT. En
efecto, los visitantes de un sitio web jamás escriben
en la base de datos. Aunque, podemos cear una tabla
contacts que servirá para registrar la dirección
email de los visitantes que recibirán un correo con
información.
Podemos tener una situación completamente inversa.
Insertamos registros con los nombres de contactos en
la tabla, sujeta a la medida de confidencialidad de
que el usuario no lea el contenido de dicha tabla.
Está tabla no es utilizada normalmente por una
aplicación pública que actualice la dirección email:

GRANT insert ON contacts TO linuxfrench_user;

Por seguridad, removemos los otros derechos


(teoricámente es inutil puesto que estos derechos no
han sido otorgados al usuario linuxfrench_user sobre
la tabla contacts) REVOKE select, update, delete,
rule ON contacts FROM linuxfrench_user;
el privilegio RULE permite crear las reglas con el
comando CREATE RULE.
Remarcamos que podemos utilizar ALL para quitar todos
los privilegios:

REVOKE ALL ON backups FROM linuxfrench_user

Los grupos

Generar derechos a nivel de usuario no es una buena


idea. De hecho, los usuarios que insertan y eliminan
son los encargados del mantenimiento de la
aplicación, por lo que es preferible crear grupos de
de usuarios. Generamos derechos a nivel de grupo, por
lo que simplemente añadimos un usuario a un grupo
cuando lo necesitemos, con lo que el usuario adquiere
los privilegios del grupo.

[localhost]# psql -U linuxfrench_dbo -d linuxfrench

Welcome to psql, the PostgreSQL interactive terminal.

Type: \copyright for distribution terms

\h for help with SQL commands

\? for help on internal slash commands

\g or terminate with semicolon to execute query

\q to quit

linuxfrench=# CREATE GROUP usuarios WITH USER linuxfrench_user;

CREATE GROUP

linuxfrench=#

hemos otorgado los derechos al grupo y creamos un


nuevo usuario, mientras que con:

linuxfrench=# REVOKE ALL ON toto FROM GROUP usuarios;

CHANGE
Le quitamos los priviliegios al grupo usuarios,
intentemos:

linuxfrench=# GRANT select ON toto TO GROUP usuarios;

CHANGE

linuxfrench=# CREATE USER linuxfrench_stagiaire WITH PASSWORD


’stagiaire’ NOCREATEDB NOCREATEUSER IN GROUP usuarios;

CREATE USER

linuxfrench=#

Probemos nuestro nuevo usuario:

[localhost]# psql -U linuxfrench_stagiaire -d linuxfrench

Welcome to psql, the PostgreSQL interactive terminal.

Type: \copyright for distribution terms

\h for help with SQL commands

\? for help on internal slash commands

\g or terminate with semicolon to execute query

\q to quit

linuxfrench=> select * from toto;

a
---
40
1
(1 row)

linuxfrench=> insert into toto values(1);

ERROR: toto: Permission denied.


linuxfrench=>

¿Una Suite...?

El material presentado en este libro de ninguna forma


trata de reemplazar o sustituir a la documentación
oficial del software libre aquí empleado, tampoco es
un tratado de forma exhaustiva de cada uno de los
temas presentados. Los apartados nos permiten
introducirnos fácilmente en el mundo del desarrollo
de apliciones WEB, PostgreSQL, PHP y HTML.
También guardo una pequeña sorpresa para el final,
después de leer el texto, encontrará que con ese
recorrido habrá aprendido haciendo. No hay normas
formales, más bien hábitos que adquiriremos con la
experiencia y a fuerza de cometer y reparar errores.
Buena suerte y bienvenidos...

Conclusión
Nos hemos iniciado en bases de datos. Basado en mi
experiencia personal trataré de darle algunos trucos
y buenos hábitos destinados a simplificar nuestra
vida.

Scripts de creación de la base de datos

En mi opinión, es imperativo proscribir las


herramientas gráficas o interactivas para crear las
bases de datos, es mejor trabajar con los scripts.
Éstos, son simples archivos de texto que contienen
las instrucciones para crear los objetos de la base
de datos. Veamos un ejemplo de tales scripts:

\echo ******************************

\echo * TABLE users

\echo ******************************

drop sequence seq_users;


create sequence seq_users;

drop table users;

create table users( user_id integer not null default nextval(’seq_users’)


primary key, user_login varchar, user_password varchar,user_profil
integer not null references profils, user_lastlogin timestamp);

\echo ******************************

\echo * TABLE sessions

\echo ******************************

drop sequence seq_sessions;

create sequence seq_sessions;

drop table sessions;

create table sessions(

...etc...

¿Cuáles son las ventajas de un script? Primero,


entendemos perfectamente la naturaleza y los tipos de
los objetos creados. Las interfaces gráficas tienen
el mal hábito de interpretar el lado obscuro de los
“meta-tipos”.
Segundo, podemos reproducir idénticamente las
operaciones de inicialización de una base iempre que
lo requiramos, a través de un simple telnet o ssh. Es
muy práctico preparar una configuración de
producción, también lo es para el desarrollo. En
efecto, los desarrolladores frecuentemente hacen
pruebas, sujetas a la casualidad. Cuando hay daños,
es suficente “ejecutar los scripts” reunidos en una
base de desarrollo propia.
En mi opinión personal, tengo el hábito de mantener 5
scripts por cada base de datos:
• create_base.sql, la creación de objetos
propiamente dicha;
• grant_base.sql, la creación de usuarios y grupos
por defecto, así como la ejecución de los GRANT
adecuados sobre los objetos de la base de datos;
• function_base.sql, creación de funciones
(procedimientos almacenados);
• init_base.sql, inserción en las tablas de
valores de referencia (lista de países...);
• go_base.sql, efectua un vacuumdb.
Precisemos que nuestro PostgreSQL depende de un
script llamado con \i script.sql en la línea de
comandes psql.
UNION / ALL desempeños muy diferentes
Cuando efectuamos una unión de consultas con la
cláusula UNION, se necesario conocer que el SGBDR va
a crear una tabla temporal para eliminar los
duplicados. Si tenemos la certeza de que nuestras
consultas generan un resultado único, entonces
podemos pensar en la cláusula UNION ALL más que en
UNION. El SGBDR enviará la salida bruta, sin tratar
de eliminar los duplicados. El desempeño mejora.

Utilidad de los alias de tablas

En nuestras consultas, utilizamos sistemáticamente


los alias de tablas. Esto hará que nuestras cláusulas
WHERE sean más simples de leer y mantener!. Por
ejemplo:

SELECT clase.nom, alumnos.nom, apellidos

FROM alumnos, clases

WHERE alumnos.clase_id = clases.clase_id

AND clases.nom LIKE ’3%’

Mientras que:
SELECT c.nom, e.nom, apellidos

FROM alumnos e, clases c

WHERE e.clase_id = c.clase_id

AND c.nom LIKE ’3%’

No presuma del resultado de una operación que tenga un NULL

Le he dicho y le repito que NULL significa


indeterminado. Pregunta ¿qué psará con NULL+1?. La
primera reflexión es:

Prueba1=# select 1+1 ;

?column?
----------
2
(1 row)
Prueba1=# select 1+null ;
?column?
----------
(1 row)
hemos dicho que NULL+x produce un NULL. ¡FALSO!.
Tenemos un resultado que puede cambiar de un SGBDR a
otro, de una versión a otra incluso en el mismo SGBDR
(incluso depende del humor del SGBDR). Cuando un
campo interviene en cálculo es susceptible de
contener el valor NULL, utiliza la función COALESCE
que retorna el primer parámetro no NULL:

Prueba1=# select 1+coalesce(null,0) ;

?column?
----------
1
(1 row)
Nombres sistemáticos de columnas

Trataremos de no utilizar SELECT * o INSERT INTO


ma_table VALUES(...). trate de nombrar explicitamente
sus columnas y asigne un nombre a sus columnas
producto de cálculo:

« SELECT valor1, valor2+valor3 valor_calculo... »

o:

« INSERT INTO ma_table(col1,col2,col3) VALUES (v1,v2,v3) ».

es un poco laborioso, pero se evitará muchos


problemas cuando el esquema de una tabla sea
modificado con ALTER TABLE.

Trabajemos siempre en forma modular

El desarrollador siempre está tentado a hacer una


consulta, como el código es de una aplicacion
cliente, para navegar el resultado de esa consulta
con el propósito de efectuar una operación sobre cada
línea. Es una mala idea. Frecuentemente ocurre que
los programas en la complejidad (el número de
operaciones ejecutadas, en terminos de desempeño) es
exponencial, alors qu’une seule requête bien conçue
aurait pu faire l’ensemble des opérations.

Piense en el producto cartesiano de una tabla sobre ella misma

Veamos un ejemplo típico en el cual muchos han optado


por un enfoque iteractivo en lugar del enfoque
ensembliste.
Consideremos la tabla siguiente. Ella contiene sus
boletos de ferrocarril. La columna depart_retour
contiene D o R dependiendo de lo su boleto de salida
(d) o de regreso (r), date_heure l’heure es la hora
de partida del ferrocarril (salida) o la de llegada
(para el regreso), y destination, lugar de nuestro
destino.

Prueba1=# \d voyedads
Table "voyedads"
Attribute | Type | Modifier
---------------+--------------------------+----------
depart_retour | character(1) |
date_heure | timestamp with time zone |
destination | character varying(128) |

Prueba1=# select * from voyedads;

depart_retour | date_heure | destination


--------------+------------------------+-------------
D | 2002-01-02 10:00:00+01 | PARIS
R | 2002-01-05 11:30:00+01 | PARIS
D | 2002-01-07 06:30:00+01 | ANGERS
R | 2002-01-08 22:25:00+01 | ANGERS
D | 2002-01-15 06:35:00+01 | PARIS
R | 2002-01-17 22:10:00+01 | PARIS
(6 rows)
Si queremos calcular la duración exacta de cada uno
de los viajes, muchos se tentarán para hacer una
primera solicitud que devolverá los boletos de salida
( SELECT * FROM voyedads WHERE depart_retour=’D’ ), y
entonces hacer desde la aplicación del cliente (en
PHP, C++, etc.) una nueva demanda que devolverá el
boleto atrás para cada uno de boleto ir encontrar
(SELECT MIN(date_heure) FROM voyedads WHERE
depart_retour =’R’ AND date_heure > ’xx-xx-xxx’) , y
calcular la diferencia de fecha, actualizando
posteriormente la aplicación cliente.
Mientras que las cosas a realizar son muy simples, en
un sólo requerimiento:

Prueba1=# select MIN(r.date_heure) - d.date_heure, d.destination

Prueba1=# FROM voyedads r, voyedads d

Prueba1=# WHERE r.date_heure > d.date_heure AND


d.depart_retour=’D’

Prueba1=# AND r.depart_retour=’R’

Prueba1=# GROUP BY d.date_heure, d.destination;

?column? | destination
--------------+-------------
3 days 01:30 | PARIS
1 day 15:55 | ANGERS
2 days 15:35 | PARIS
(3 rows)

Prueba1=#

En este requerimiento, uno ubica dos veces la misma


tabla en la cláusula FROM, para asegurarnos bien
utilizamos un alias en cada una de las tablas con los
nombres de r y d para poder diferenciarlos en la
consulta.
Llamamos a estas dos tablas en la cláusula FROM un
producto cartesieno. Es decir, que para cada uno de
los elementos de la primera, el SGBDR devuelve todos
los elementos de la segunda.
Como requerimos un cálculo para cada una de las
salidas en un viaje, la primera estructura es simple:
requerimos las salidas donde WHERE:
d.depart_retour=’D’.
para la segunda estructura, requerimos el regreso
donde la fecha ocurre inmediatamente después de la
fecha de salidad considerada.
Para « inmediatamente después » quiero decir que está
sobre el regreso más cercano cuya fecha es posterior
a la fecha saliente.
Donde, como principal, queremos los regresos:

WHERE r.depart_retour=’R’.

El regreso debe ser posterior a la salida, por lo que


debemos considerar:

WHERE r.date_heure > d.date_heure.

Tenemos como requerimiento una primera estructura


para todas las salidas y para una de ellas, devolverá
una segunda constituida de todos los ingresos
posteriores a esta salida. No es nuevo lo qué uno
quiere, pero podemos ejecutar la demanda para
verificar nuestro resultado:

Prueba1=# SELECT d.date_heure as depart, r.date_heure as retour


Prueba1=# FROM voyedads d, voyedads r

Prueba1=# WHERE d.depart_retour=’D’ AND r.depart_retour=’R’

Prueba1=# AND r.date_heure > d.date_heure;

depart (salida) | retour (llegada)


------------------------+------------------------
2002-01-02 10:00:00+01 | 2002-01-05 11:30:00+01
2002-01-02 10:00:00+01 | 2002-01-08 22:25:00+01
2002-01-02 10:00:00+01 | 2002-01-17 22:10:00+01
2002-01-07 06:30:00+01 | 2002-01-08 22:25:00+01
2002-01-07 06:30:00+01 | 2002-01-17 22:10:00+01
2002-01-15 06:35:00+01 | 2002-01-17 22:10:00+01
(6 rows)

Prueba1=#

encontramos en la columna depart nuestras tres


salidas diferentes y en la columna de la derecha,
retour, los regresos a cada una de estas salidas.
Pero queremos la salida más breve (en terminos de
date) de regreso para cada una de las salidas
consideradas, el agregado MIN(date_heure) es el
indicado. Sólo nos resta conocer que criterio de
agrupamiento utilizaremos para efectuar el agregado,
y eso está correcto porque queremos el regreso más
breve para cada una de las salidas, simplemente:
GROUP BY d.date_heure, d.destination.
De manera general, las uniones (join) de una tabla
sobre ella misma son muy prácticas porque es una
forma de contar registros de la misma tabla.
Tercera parte
Historia de PHP
Ningún libro puede omitir una breve historia de PHP.
Rasmus Lerdorf lo escribió en el otoño de 1994. La
primera versión disponible para el público estuvo
lista a principios de 1995 y fue conocida como
"Herramientas para paginas web personales" (Personal
Home Pedad Tools). Éstas, consistían en un analizador
sintáctico muy simple que sólo entendía unas cuantas
macros y una serie de utilidades comunes en las
páginas web de entonces, un libro de visitas, un
contador y otras pequeñas tareas.
Hoy en día (principios de 2003), tanto PHP4 se
distribuye en un gran número de productos comerciales
tales como el servidor web "C2’sStrongHold", Linux
Mandrake y RedHat Linux. Una estimación conservadora
nos indica que más de 1.000.000 de servidores
alrededor del mundo utilizan PHP. A la vez que todo
esto está pasando, el trabajo de desarrollo de la
próxima generación de este lenguaje está en
desarrollo utilizando el potente motor de scripts
Zend para proporcionar altas prestaciones, así como
soportar otros servidores web, además de apache, que
corren PHP como módulo nativo.

Introducción al PHP
PHP, es acrónimo de "PHP: Hypertext Preprocessor",
que engloba lo que originalmente significa Personal
Home Pedad Tools.
Es un lenguaje interpretado de alto nivel embebido a
páginas HTML. PHP nos sirve principalmente para
realizar páginas html dinámicas de forma sencilla, ya
que podemos extender su uso a través de los shell-
scripts, recuerde que existen interpretes en línea de
comandos.
Por html dinámico entendemos el generador o
preprocesador para el interprete de PHP que como
salida nos proporciona una página en html.
PHP es muy parecido al lenguaje C, Java y Perl.
Básicamente las diferencias son: PHP no es sensible a
mayúsculas y minúsculas, las excepciones en el
tratamiento de objetos y variables, en que no se
declaren las variables y no tienen un tipo fijo, una
variable puede albergar diversos tipos de datos y a
lo largo de su vida pueden variar.
Por otro lado, debemos tener en cuenta que PHP se
ejecuta en un servidor y sobre demanda (normalmente
submits) por el que cambia la filosofía de las
aplicaciones convencionales con una interfase con el
usuario más orientada a eventos!
¿Qué es PHP?
PHP es un lenguaje de programación embebido en HTML e
interpretado por el servidor. Es embebido ya que el
código PHP se encuentra dentro de código HTML, aunque
eso sí, delimitado por secuencias de escape.

<html>

<?php

echo "Esto es código PHP";

$c = 2;

$a = 4 + 5 + $c;

echo $a; ?>

</html>

Y por otro lado es el servidor el que se encarga de


interpretar el código PHP generando el resultado, por
lo que el cliente tan solo verá el código HTML que
generó la secuencia de PHP. Podemos pensar en
JavaScript como contrapunto a PHP en este sentido.

¿Qué hacemos con PHP?


Con PHP podemos hacer lo mismo que cualquier programa
CGI. Pero el uso más generalizado que se le da a PHP
es el de acceso a bases de datos. En este aspecto PHP
ofrece interfaces con gran número de sistemas de
gestión de bases de datos. A partir de la versión 3,
se soporta a:
• Adabas D.
• dBase .
• Empress.
• FilePro.
• Informix.
• Interbase.
• MSQL.
• MySQL.
• Oracle.
• PostgreSQL.
• Solid.
• Sybase.
• Velocis.
• Unix dbm.
Además, PHP es capaz de acceder con una interfase
sencilla a servicios tan importantes y avanzados como
son POP3, IMAP, SNMP, HTTP, NNTP... e incluso permite
el manejo de sockets de conexión para interactuar con
otros protocolos.
A nivel básico, PHP puede hacer cualquier cosa que se
pueda hacer con un script CGI, tal como procesar
formularios, generar, páginas con contenido dinámico,
enviar y recibir cookies o interactuar con el
sistema: borra archivos, crea...
La mayoría de las funciones más útiles ya están
predefinidas:
• Conectividad: HTTP, FTP, COM, YP/NIS, SNPM,
Sockets, CORBA, LDAP.
• Servicios de correo y noticias: POP, IMAP, SMTP,
NNTP.
• Texto y gráficos: XML, HTML, PDF, GD, Flash.
• Funciones matemáticas.
• POSIX: semáforos, memoria compartida, acceso a
fitxers, expresiones regulares, cronómetros.
• Comercio electrónico: Cybercash, Verisign.
• Formularios.
• Encriptación y compresión: MD5, Gzip, Bzip2,
OpenSSL.
Ventajas de PHP
Es multiplataforma, se ejecuta en Linux, Windows,
Unís, MacIntosh, Solaris, etc..

El intérprete
Es destacable el hecho de que el intérprete de PHP
pueda ser:
• Un módulo que el propio servidor web cargue y
gestione, existiendo módulos para Apache, IIS o
PWS;
• Un programa CGI.
La elección dependerá de nuestras necesidades:
mientras que la segunda opción ofrece un mayor
rendimiento, la primera se muestra más segura.
Configuración de PHP
PHP es un lenguaje altamente configurable, es decir,
podemos condicionar el comportamiento del intérprete
en muchos aspectos por medio de modificar y añadir
directivas en su archivo de configuración o en el de
Apache. En este apartado vamos a estudiar las
directivas más importantes y a lo largo del libro nos
daremos cuenta de la utilidad de algunas de ellas.

Archivos de configuración
Los Archivos de configuración para PHP3 y PHP4 son,
respectivamente, php3.ini y php.ini. Si utilizamos el
intérprete de PHP como módulo del servidor web,
entonces el archivo de configuración se lee cuando se
inicia dicho servidor. Si, por el contrario, trabaja
como CGI, el archivo se lee cada vez que se ejecute
el intérprete.

Directivas de configuración
En la siguiente lista se explica el comportamiento de
algunas de las directivas de configuración más
importantes. En rojo tenemos su nombre y en verde el
tipo de valor que almacena o regresa.

asp_tags
lógica
Permite o no la utilización de marcas al estilo ASP.
Ver enlace con escape de html

display_errors
lógica
Indica si los errores han de escribirse como parte
del documento HTML.
doc_root
cadena
Si PHP está configurado en modo seguro no servirá
ningún documento fuera de este directorio.
error_log
cadena
Nombre del archivo donde se notificarán los errores.

error_reporting
entera
Determina el nivel de notificación de errores. Ver
manejo de errores.

open_basedir
cadena
Indica el árbol de directorios donde se encuentran
los Archivos que PHP puede abrir. Bien utilizado,
supone una nueva medida de seguridad.

include_path
cadena
Especifica la lista de directorios donde require() e
include() buscarán los Archivos.

max_execution_time
entera
Fija el máximo tiempo de ejecución de un script
escrito en PHP. Con ello se consigue que, si alguno
de los scripts falla, no se quede "colgado" ocupando
recursos.

memory_limit
entera
Fija la cantidad máxima de memoria que puede tomar un
script.

track_errors
lógica
Especifica, si su valor es verdadero, que el último
mensaje de error se almacena en $php_errormsg.
track_vars
lógica
Si es verdadera, indica que las entradas
correspondientes a GET, POST y cookie se encuentren
en los vectores globales asociativos $HTTP_GET_VAR,
$HTTP_POST_VARS y $HTTP_COOKIE_VARS, respectivamente.

Más directivas
Las directivas que hemos visto no son todas las que
admite la configuración de PHP; encontramos más
directivas específicas para seguridad, correo y las
diferentes bases de datos. No obstante, no es objeto
de este libro un estudio exhaustivo de la
configuración del lenguaje, así que las directivas
restantes se analizarán en otra ocasión.
Primeras nociones de sintaxis
Escape
Si hablamos de la sintaxis de PHP tendremos que
empezar, sin ningún genero de dudas, por las
secuencias de escape, lo que nos permite delimitar el
código PHP dentro del HTML.
Como podemos ver en el ejemplo 2 nos encontramos con
varias posibilidades:
• Utilizamos la marca de comienzo <? y marcamos el
final con ?>.
• Empezamos con <?php y finalizamos con ?>
• Hacemos uso de la marca <SCRIPT> tal y como se
muestra en el ejemplo.
• Pueden usarse también marcas estilo ASP.

<? echo("Esto es código PHP"); ?>

<?php echo("Esto también es código PHP"); ?>

<SCRIPT LANGUAGE="php"> echo("Más código PHP"); </SCRIPT>

<% echo("El último ejemplo"); %>

Separación de instrucciones
La separación de instrucciones, como puede
observarse, se lleva a cabo mediante el carácter ";",
algo común a muchos de los lenguajes convencionales.

Comentarios
En PHP se admiten los comentarios del tipo C, C++ y
shell (estilo sh o bash). Vamos a ponerle comentarios
al ejemplo 1:
<html>

<?php

echo "Esto es código PHP"; /* Estilo C */

$c = 2; // Estilo C++

$a = 4 + 5 + $c; # Estilo shell

echo $a; ?>

</html>
Tipos
Los tipos disponibles en lenguaje PHP son:
• Tipos simples
o Enteros
o Reales (equivalentes a los doubles de C).
o Cadenas
• Tipos compuestos
o Vectores
Asociativos
Escalares
• Objetos

Enteros
PHP soporta tres notaciones diferentes para
especificar los números enteros: octal, decimal y
hexadecimal.
Octal: 075
Decimal: 276
Hexadecimal:0x0A3

Vemos que es necesario especificar de forma explícita


las notaciones octal y hexadecimal, mediante los
caracteres "0" y "0x", respectivamente. Si no se
indica nada se asume que se trata de notación
decimal.

Reales
Los reales, aunque no admiten notaciones en
diferentes bases como los enteros (bases 8, 10 y 16),
pueden especificarse de las dos formas siguientes:

2.432
2.33e3 (notación científica)
Hay que hacer notar el hecho de que los reales en PHP
equivalgan en rango y precisión a los reales tipo
double que encontramos en C.

Cadenas
Las cadenas podemos especificarlas en PHP de dos
formas distintas: encerrándolas entre comillas dobles
(") o simples ('). Dependiendo de la forma de
especificarlas, nos encontraremos con dos
comportamientos diferentes.
Si utilizamos comillas dobles las variables
contenidas en la cadena se sustituirán por su valor
y, además, podremos utilizar caracteres de escape al
igual que podemos hacer en C. Aclaremos esto con un
ejemplo:

$a = 10;

$b = 1.2e3;

$c = "Los valores de \"a\" y \"b\" son $a y $b.";

echo("$c");

El resultado de la ejecución del fragmento de código


anterior será:

Los valores de "a" y "b" son 10 y 1200.

Nótese que se han utilizado caracteres de escape para


poder incluir comillas dobles en la cadena.

\n Retorno de carro

\t Tabulación

\\ \

\$ $

\" "

Si utilizamos comillas simples las variables no serán


sustituidas por su valor y, además, solo podrán
usarse las secuencias de escape "\\" y "\'".
$a = 10;

$b = 12;

$c = 'Los valores de \'a\' y \'b\' son $a y $b.';

echo('$c');

echo("$c");

La salida generada será:

$c Los valores de 'a' y 'b' son $a y $b.

Conversión de cadenas
Podemos evaluar las cadenas como valores numéricos.
Se fijan los siguientes criterios:
• Si la cadena contiene '.', 'e' o 'E', se evaluará
como un número real. En otro caso se considerará
un entero.
• El valor de la conversión será el fragmento
inicial de la cadena, si este es número válido. Se
considerará que se trata de un número válido si
está compueso de un signo (opcional), uno o más
dígitos, punto decimal (opcional) y un
exponente(también opcional) que consiste en una
'e' o 'E' seguida de más dígitos.

$valor = 1 + "2"; # $valor = 3;

$valor = 3 + "4.5"; # $valor = 7.5;

$valor = 4 + "3.1e3 prueba"; # $valor = 3104;

Vectores
PHP nos permite la creación de vectores de forma
implícita (al utilizarlos se declaran
automáticamente) o bien de forma explícita (funciones
list() o array()).
Al igual que gran parte de los lenguajes, en PHP se
accede a una posición de un vector poniendo su nombre
y el índice encerrado entre corchetes [ ]. El índice
puede ser asociativo $a["rojo"] o escalar $a[4]. Si
al asignar un valor a un vector no ponemos ninguna
posición entre los corchetes, estaremos añadiendo una
posición al final del vector donde se almacenará
dicho valor.

$a[0] = "elemento 0"; /* vectores con índice escalar */

$b[4] = 3;

$c["rojo"] = "cadena"; /* vectores con índice asociativo


*/

$d['verde'] = "color";

$b[] = 7; /* almacenamos un 3 en la posición 5 de $b */

El manejo de vectores multidimensionales se basa,


simplemente, en la adición de otro índice:

$a[3]["b"] = 3;

Desafortunadamente en PHP3 no es posible referirse a


la posición de un vector multidimensional
directamente dentro de una cadena, es decir, el
siguiente ejemplo no va a dar el resultado esperado.

$a[4]['prueba'] = 'prueba';

echo ("Esto es una $a[4]['prueba']");

El resultado será esto es una Array[prueba] que no es


lo que se esperaba. De todas formas existen diversas
soluciones como volcar el valor en una variable que
no sea un vector multidimensional o construir la
cadena y después mostrarla.
Pero en PHP4 aparecen las llaves { } para solucionar
el problema de la siguiente forma:

echo ("Esto es una {$a[4]['prueba']}");


Objetos
Para poder seguir este apartado es necesario que el
lector tenga nociones de lo que es la programación
orientada a objetos, ya que esto solo pretende ser un
libro de PHP, y no un libro de programación.
Una clase es un conjunto de variables al que se
asocia una serie de funciones. Se utiliza la
sintaxis:

class nombre {

var variables;

declaración y definición de funciones;

Lo veremos más claro con un ejemplo. Vamos a crear un


objeto que represente un vector:

class vector {

var $x, $y; /* componentes */

function vector($cx = 0, $cy = 0) /* constructor*/

$this->x = $cx;/* con "this" este objeto */

$this->y = $cy;

function imprime(){

print("($this->x, $this->y)");

function pone_x($cx){

$this->x = $cx;

function pone_y($cy){
$this->y = $cy;

En el ejemplo tenemos dos variables y cuatro


funciones. La primera de ellas es el constructor
(tiene el mismo nombre que el objeto) y observamos
que tiene valor por defecto para sus parámetros.
Vamos a ver a continuación como utilizar esta clase.

$v = new vector; /* inicializa la clase con los valores

por defecto */

$v->imprime();

$v->pone_x(5);

$v->imprime();

La salida del fragmento de código anterior es:

(0,0)(5,0)

En PHP también existe la herencia. Se lleva a cabo


mediante la palabra reservada extends, e implica que
todos los atributos y funciones de la clase base los
tendrá también la clase heredera. Vamos a añadirle un
punto de origen a la clase anterior.

class vector_extendido extends vector {

var $ox, $oy;

function vector_extendido($cx = 0,$cy = 0,$ox = 0, $oy =


0){

$this->x = $cx;

$this->y = $cy;

$this->ox = $ox;

$this->oy = $oy;

function imprime_o(){
print("($this->ox, $this->oy)");

function pone_ox($ox){

$this->ox = $ox;

function pone_oy($oy){

$this->oy = $oy;

Un posible código que utilice la nueva clase podría


ser:

$v = new vector_extendido(1,2,0,0);

$v->pone_x(5);

$v->pone_ox(2);

$v->imprime();

$v->imprime_o();

Y su salida es:

(5,2)(2,0)
Variables
Referencia a variables
Tal y como hemos venido observando en los ejemplos,
las variables en PHP se referencian mediante el
símbolo $ seguido del nombre de la variable. Además,
curiosamente, PHP es sensible a mayúsculas en cuanto
a nombres de variables se refiere; es decir, las
variables $prueba y $Prueba se consideran diferentes.
No ocurre lo mismo con el resto de construcciones del
lenguaje:

<?PHP /* puedo poner php en mayúsculas */

$prueba = "hola";

$Prueba = "amigo";

ECHO("Prueba: $prueba $Prueba"); /* ECHO también


en mayúsculas

?>

El resultado de interpretar el código anterior será:

Prueba: hola amigo

Asignación de valores
Hemos visto en ejemplos anteriores que la asignación
de valores a variables se lleva a cabo mediante el
operador =, colocando la variable destino a la
izquierda de la asignación (lvalue) y la expresión a
la derecha (rvalue).
Pues bien, en PHP3 la asignación se produce por
valor; es decir, el valor de la expresión que se
encuentra a la derecha se copia en la zona de memoria
reservada para la variable destino. Esto implica, que
si asignamos el una variable var1 a otra var2, lo que
se copiará en ésta última será el valor de la
primera, y las modificaciones que hagamos sobre
cualquiera de las dos no afectarán a la otra.
$a = 5;

$b = $a;

$b = $b + 3;

/* los valores serán $a = 5 y $b = 8; */

Sin embargo, en PHP4 se admite a la asignación por


referencia; la variable var2 se convierte en un alias
de var1, y todas las modificaciones sobre una de
ellas se reflejarán en la otra. Esta tipo de
asignación se lleva a cabo colocando el carácter &
delante de la variable origen.

$a = 5;

$b = &$a;

$b = $b + 3;

/* los valores serán $a = 8 y $b = 8; */

Importante: el operador & solo se utiliza con


variables. Los siguientes usos no son válidos:

$a = &(3 + 4);

$b = &mostrar() /* mostrar() es una funcion */

$c = &$vector[3];

Ámbito de las variables


El ámbito de una variable es el conjunto de
sentencias en las que dicha variable puede
utilizarse. Por ejemplo, en la siguiente función el
ámbito de $var tan sólo comprenderá las sentencias
que conforman dicha función.

function ambito() {

$var = 4;

$a = $a + 4;

{...}
/* aquí podemos referenciar $var y obtendremos

4 como su valor */

$function no_ambito() {

/* aquí no podemos acceder al valor de $var de ambito().

Si intentamos referenciar a $var aquí, obtendremos una

variable nueva que no será $var de ambito(). */

Además, aunque declaremos $var fuera de la función no


podremos acceder a su valor sin más:

$var = 4;

function muestra() {

echo($var);

/* nos referimos a una variable $var que es local a la


función, no a la global */

Variables globales
Pero eso no significa que no podamos acceder a
variables globales. Para hacerlo debemos declarar,
como tal, a la variable global a la que queramos
acceder.

$var = 4;

function muestra() {

global $var;

echo "El valor de var es $var"; }

El resultado será:

El valor de var es 4
Otra posible solución es utilizar la variable
$GLOBALS, que es un vector asociativo que almacena
los valores de todas las variables globales.

$var = 4;

function muestra(){

$var = $GLOBALS["var"];

echo "El valor de var es $var";

Variables estáticas
Se trata de variables locales a las funciones pero
que mantienen su valor cuando se sale de la función.
Así, al volver a entrar, sigue manteniendo el mismo
valor que tenía al dejar la función.

function incrementa(){

static $var = 0;

echo("$var");

$var = $var + 1;

Después de dos llamadas a la función, var tendrá el


valor 2.

Variables variables
Podría parecer que, en un principio, el título de
este capítulo está mal escrito... pero no es así. Si
utilizamos dos $ en lugar de uno, obtendremos el
siguiente resultado:

$b = "saludo"

$a = "b";

echo "${$a}";
En la última línea, PHP tomará el valor de $a, que es
b, y lo sustituirá. Entonces tenemos la sentencia
equivalente echo $b, que será "saludo". Incluso
podremos hacer una asignación del tipo $$a =
"despedida", que modificará el valor de $b.

Variables predefinidas
En PHP se definen por defecto una serie de variables
a las que podemos acceder desde nuestros scripts. En
esta sección vamos a ver las variables de entorno,
que resumimos en la siguiente tabla.

Vector con los argumentos


pasados al script. Cuando se
argv llama mediante el método GET,
este vector contiene la cadena
de petición.

Número de parámetros contenidos


argc
en argv.

Nombre de archivo del script en


ejecución. Solo disponible
PHP_SELF
cuando el intérprete es un
módulo del servidor web.

Vector asociativo de variables


pasadas por medio de HTTP
HTTP_COOKIE_VARS cookies. Solo disponible si la
directiva track_vars está
activada.

Vector asociativo que contiene


las variables pasadas por medio
HTTP_GET_VARS
del método GET. Disponible solo
si track_vars está activado.

Vector asociativo que contiene


las variables pasadas por medio
HTTP_POST_VARS
del método POST. Disponible solo
si track_vars está activado.
Constantes
Con PHP podemos definir constantes gracias a la
función define(). Su sintaxis es la siguiente:

define("nombre_cte", valor);

Ejemplos:

define("CTE1", "Valor de la constante");

define("CTE2", 45);

echo(CTE1);

Vemos que en el caso de las constantes no es


necesario poner el símbolo $. Nótese que una
constante no podrá aparecer dentro de una cadena.
Operadores
PHP nos ofrece una gran variedad de operadores, desde
los aritméticos hasta operadores a nivel de bits.
Podemos llevar a cabo la siguiente clasificación:
• Aritméticos
• Lógicos
• De comparación
• Lógicos
• De cadenas
• A nivel de bits
• De asignación
• De incremento y decremento
• De control de errores
• De ejecución

Operadores aritméticos

Operación Símbolo Devuelve Ejemplos

Suma + La suma de los operandos $a + $b

Resta - La diferencia de los operandos $a - $b

Producto * El producto de los operandos $a * $b

División / El cociente de los operandos $a / $b

Módulo % El resto del cociente $a / $b $a % $b

Operadores lógicos
En principio nos va a llamar la atención el hecho de
que existan dos operadores AND y dos OR. La
explicación es que cada operador AND tiene diferente
recedencia; con los operadores OR ocurre lo mismo.
Operación Símbolo Devuelve Ejemplos

Verdadero si $a y $b son $a and


Y-lógico and
verdaderos $b

Verdaderos si $a y $b son
Y-lógico && $a && $b
verdaderos

O-lógico or Falso si $a y $b son falsos $a or $b

O-lógico || Falso si $a y $b son falsos $a / $b

Verdadero si $a o $b es $a xor
O-exclusivo xor
verdadero, pero no los dos $b

Negación
! Verdadero si $a es falso. !$a
lógica

Operadores de comparación
Operación Operador Devuelve Ejemplos

Verdadero si $a y $b son
Igual == $a == $b
iguales

Verdaderos si $a y $b
Identidad === son verdaderos y del $a && $b
mismo tipo

Verdadero si $a y $b son
Distinto != $a != $b
distintos

Verdadero si $a es menor
Menor que < $a < $b
que $b

Menor o igual Verdadero si $a es menor


<= $a <= $b
que o igual que $b

Verdadero si $a es mayor
Mayor que > $a > $b
que $b
Mayor o igual Verdadero si $a es mayor
>= $a >= $b
que o igual que $b

(Expr1) ?
Exp2 si Exp1 es
Condicional ?: (Expr):
verdadero; si no Expr3
(Expr3);

Operadores de cadenas
El operador ofrecido por PHP para las cadenas es el
de concatenación, que se corresponde con el
símbolo"." (punto).
También consideramos a los corchetes [ ] como
operador para las cadenas, ya que cumple una función
análoga a la que lleva a cabo con los vectores.

$c = $a.$b; /* $c contendrá la concatenación $a y $b */

$cadena = "prueba";

$caracter = $cadena[3]; /* $caracter valdrá 'e' */

Operadores a nivel de bits


Operación Operador Devuelve Ejemplos

Y-Lógico & Y-Lógico bit a bit $a & $b

O-Lógico | O-Lógico bit a bit $a | $b

O-Exclusivo a nivel de
O-Exclusivo ^ $a ^ $b
bits

Negación ~ Negación a nivel de bits ~$a

Desplaza $a hacia la
Desplazamiento a la
<< izquierda a nivel de $a << $b
izquierda
bits $b veces

Desplaza $a hacia la
Desplazamiento a la
>> derecha a nivel de bits $a >> $b
derecha
$b veces
Operadores de asignación
El operador de asignación por excelencia es "=". Se
trata del más sencillo de los operadores de
asignación:

$a = $b + 1; /* $a vale $b + 1 */

Decimos que es el más sencillo porque PHP, al igual


que C, nos ofrece gran variedad de operadores de
asignación, por medio de la combinación del símbolo
"=" con otros operadores.

$a += 5; /* $a = $a + 5; */

$b .= "Concatena"; /* $b = $b."Concatena"; */

$a -= 3; /* $a = $a - 3; */

$a *= $a /* $a = $a * $a; */

$a /= 2; /* $a = $a / 2; */

Otro aspecto a destacar de los operadores de


asignación en PHP es que, además de producirse la
asignación de un valor, se devuelve él mismo.

$a = ($b = 3);/* $b = 3 da 3. Por tanto $a = $b = 3; */

$a = $b = 3;

$a = ($b *= 3) + 5; /* $b = $b * 3;

$a = $b * 3 + 5; */

Operadores de incremento y decremento


(++, --)
Estos operadores han estado siempre ligados a C,
lenguaje con el cual vieron la luz. Se trata de
incrementar en una unidad el valor de la variable
sobre la que se aplican: $a++ equivale
aritméticamente a $a = $a + 1, o bien, a $a += 1.
Pero estos operadores tienen algo más: si se colocan
antes de la variable (preincremente/ predecremento)
se incrementa/ decrementa el valor de $a y luego se
utiliza su valor. Si se ponen como sufijo de la
variable, primero se utiliza el valor de $a y luego
se opera sobre ésta.

$a = 1;

$a++; /* $a vale 2 */

$a--; /* $a vale 1 */

++$a; /* $a vale 2 */

--$a; /* $a vale 1 */

Hasta aquí, nada especial. Pero en el siguiente


ejemplo vemos lo que explicábamos, la importancia de
su posición:

$a = 1; /* $a vale 1 */

$b = $a++; /* $b vale 1 y $a vale 2 */

Primero se utiliza el valor de $a para la asignación


y, después, se incrementa $a.

$a = 1; /* $a vale 1 */

$b = --$a; /* $a y $b valen 0 */

Primero se decrementa $a y luego se utiliza su valor


en la asignación.

Operador de control de errores


El signo @, colocado antes de una expresión, hace que
cualquier mensaje de error de esa expresión se
ignore. Dicho mensaje se almacenará en la variable
global $php_errormsg.

Operador de ejecución
Por medio de la utilización de backquotes `, se
comunica a PHP la ejecución de un comando del shell.

$date = `date`;

echo $date;
/* la salida será Fri Feb 14 00:54:51 WEST 2000 */
Estructuras de control
PHP hereda gran parte de la sintaxis de sus
estructuras de control de las del lenguaje C, aunque
añade algunas características diferentes

if, else, elseif


Esta es una de las estructuras más conocidas, y está
incluida, en una u otra forma, en todos los lenguajes
de programación modernos de alto nivel. Su sintaxis
es:

if (condicion)

sentencias1;

[elseif (condicion)]

sentencias2;

[else]

sentencias3;

siguente instrucción...

La palabra reservada if aparece una sola vez; elseif


puede aparecer cuantas veces sea necesario; y else,
como mucho, una vez. Su interpretación es la
siguiente:
• si la condición de if es verdadera, entonces se
ejecuta el bloque de sentencias 1 y se sigue por
la siguiente instrucción a la estructura.
• si la condición de if no es verdadera, se evalúan
la condición del siguiente elseif (si existiera).
Si se cumple se ejecuta el bloque de sentencias
correspondiente y si no se pasa al siguiente
elseif. Cuando alguna de las condiciones se
cumpla, se ejecuta el grupo de sentencias
correspondiente y se sale de la estructura.
• si ninguna de las anteriores condiciones se
cumple, y si existe cláusula else, se ejecuta su
bloque de sentencias.
En esta y en todas las estructuras de control, si un
grupo de sentencias tiene más de una sentencia, se
encierran entre llaves { }.

if ($a == $b) {

$a++;

$b++;

elseif (($a < $b)&&($c = 1))

$c = 0;

elseif (($a < $b)&&($c = 0))

$c = 1;

else {

$a = $b = $c = 0;

while
Es la implementación del bucle con condición inicial.
Su sintaxis es:

while (condicion)

sentencias;

Se evalúa la condición y si se cumple se ejecuta el


grupo de sentencias. Al terminar se vuelve a evaluar
la condición y, si vuelve a cumplirse, se entra de
nuevo en la ejecución de las sentencias. Se sale del
bucle cuando una de las comprobaciones de la
condición falla.

/* El siguiente bucle imprime los número de 0 a 99 */

while ($i < 100)

echo $i++;

/* El siguiente código equivale al anterior */


while ($i < 100) {

echo $i;

$i++;

do ... while
Se diferencia del bucle while por el hecho de que la
condición se empieza a comprobar después de la
primera iteración (con condición final). Por tanto,
el grupo de sentencias se va a ejecutar, al menos,
una vez.

do

sentencias;

while (condicion);

Veamos dos ejemplos y se deja como ejercicio para el


lector averiguar qué hacen estos bucles

$i = 0;

do

echo ++$i;

while ($i < 50);

$i = 0;

do

$i++;

echo $i;

while ($i < 50);

for
Se trata de, probablemente, el bucle más poderoso.
Tiene el mismo comportamiento que en C. Su sintaxis
es la siguiente:
for (expresion1; expresion2; expresion3) sentencias;

La primera expresión se ejecuta al principio del


bucle, de forma incondicional. Al inicio de cada
iteración, se evalúa la expresión 2: si es verdadera
se sigue iterando; si es falsa se sale del bucle. Al
final de cada iteración se ejecuta expresion.

/* Un ejemplo sencillo para un vector de n elementos con 0


*/

for ($i = 0; $i < $n; $i++)

a[$i] = 0;

Pero esta estructura puede complicarse hasta límites


insospechados:

for ($i = 0, $j = 100; $i < $n, $j < $m; $i++, $j += 2)

...

Su sintaxis permite escribir bucles infinitos como


este:

/* suele combinarse con algún break */

for (;;) {

...

E incluso:

for (;;$i++) {

if ($i < 100) break;

echo $i;
}

foreach
En PHP4 se dispone de una estructura de control que
permite iterar de forma muy cómoda sobre un vector.
Tiene dos posibles sintaxis:

foreach(vectoras$var) sentencias;

foreach(vectoras$clave => $valor) sentencias;

El primer bucle va recorriendo el vector colocando en


$var el valor de la posición actual (por medio del
puntero interno del vector). En cada iteración se
ejecutan las sentencias.
La segunda expresión difiere de la primera en que
también se coloca el valos de clave en $clave,
iterando de la misma manera.

switch
Equivale a un grupo de if combinados:

switch (expresion) {

case valor1:

sentencias;

[break;]

case valor2:

sentencias;

[break;]

case valor3:

sentencias;

[break;]

...

[default: sentencias;]
}

En esta estructura se evalúa la expresión y se


compara, por orden, con los valores. Si coincide, se
ejecuta el grupo de sentencias correspondiente. Si no
es así se pasa al siguiente valor. Puede usarse el
break para detener salir del switch.

switch ($i % 4) {

case 0: $i = 4;

break;

case 1: /* equivale a (($i % 4) == 1


||

($i % 4) == 2)

case 2: $i = 0;

break;

default:

$i++;

break
Como se ha visto, termina con la ejecución de la
estructura if, for, while o switch actual. Si se le
pasa un parámetro numérico se le indica de cuántos
niveles de anidamiento ha de salir.

$i = 0;

while (++$i)

switch ($i % 3) {

case 0: echo "$i divisible entre 3";

break 1;/* sale del switch */

case 1: echo "Sobra 1 en la división";


break 2;/* sale fuera del bucle */

case 2: echo "Sobran 2 en la división";

continue
Se utiliza en los bucles para saltarnos el resto de
la iteración actual y comenzar con una nueva. También
admite un parámetro que indicará el número de niveles
de bucle que ha de saltarse.

while ($i < $n) {

for (;;) {

if (($i % 2) == 0) continue 2;

/* se salta lo que queda de este bucle y del siguiente */

$j[$i] = 0;

do {

$k++;

if ($k == $j) continue;

/* solo se salta el final de este bucle */

$k[$j] = 0;

while ($k != $i);

$i++;

require() e include()
La estructura require() equivale a la directiva
#include del lenguaje C; se sustituye ella misma por
el contenido del archivo pasado como argumento. En el
caso de requiere() el archivo se lee como si fuera
código HTML, así que si queremos que algo se ejecute
debe estar marcado como código PHP.
Por su parte, la estructura include() ejecuta el
código contenido en el archivo, y acepta como
parámetro incluso una variable con el nombre del
fichero.
Funciones
Definición de funciones
La sintaxis de definición de funciones es bastante
simple:

function nombre_funcion(argumentos) {

cuerpo de la función

Un ejemplo:

function incrementa($i) {

$i++;

echo 'Variable $i incrementada...'

Paso de parámetros
Hay varios aspectos a comentar a cerca de los
argumentos de las funciones. Para empezar, PHP
soporta el paso de parámetros por:
• valor: se hace una copia del valor pasado en una
nueva variable que será local a la función, y
cuya modificación no afectará a la variable
pasada.
• referencia: la variable local que recoge el
argumento se convierte en un alias de la
variable pasada. Toda modificación que sufra su
valor dentro de la función tendrá su reflejo en
la variable original.

Paso de parámetros por valor


El paso de parámetros por valor es el que se lleva a
cabo por defecto en PHP. No es necesaria ninguna
sintaxis ni indicación especial. Por ejemplo,
declaramos la siguiente función:

function incrementa($parametro1) {

$parametro1++;

echo "Incrementando parametro a $parametro1";

Entonces, con la siguiente llamada llevaremos el paso


del parámetro por valor:

$cuenta = 0;

incrementa($cuenta);

echo "El valor de cuenta es $cuenta";

Obtenemos el siguiente resultado:

Incrementando parametro a 1

El valor de cuenta es 0

Podemos observar que el valor de $cuenta sigue siendo


el mismo antes y después de la ejecución de la
función, a pesar de que $parametro1 haya modificado
su valor.

Paso de parámetros por referencia


Para el paso de parámetros por referencia es
necesaria una modificación en la sintaxis al declarar
o utilizar la función. Si quisiéramos pasar la
variable $cuenta por referencia tenemos dos opciones.
La primera es poner el símbolo & antes del parámetro
correspondiente en la declaración de la función.

function incrementa(&$parametro1){

$parametro1++;

echo "Incrementando parametro a $parametro1;

}
La segunda es poner el símbolo & cuando se llame a la
función.

$cuenta = 0;

incrementa(&$cuenta);

En ambos casos los resultados obtenidos serán


idénticos, produciéndose la modificación del valor de
$cuenta:

Incrementando parametro a 1

El valor de cuenta es 1

Devolución de valores
Las funciones pueden devolver valores, fruto de su
ejecución. Dichos valores pueden asignarse a
variables o, por el contrario, ignorarse. Para la
devolución de un valor se utiliza la palabra
reservada return.

funtcion suma($a, $b) {

return $a + $b;

/* devuelve la suma de $a y $b

$resultado = suma(4, 3);

/* el resultado, 7, se almacena en $resultado */

suma($resultado, 44);

/* en este caso el resultado se ignora */

Valores por defecto


En PHP, al igual que en C++, pueden asignarse valores
por defecto a los argumentos de una función. Ello
significa que si, al llamar a la función, no se
especifica ningún valor para el argumento, entonces
se asume su valor por defecto.

function incrementa($numero = 1) {
$numero++;

return "Devolviendo un $numero";

echo incrementa();

echo incrementa(5);

En la primera llamada el valor de $numero será 1,


tomando su valor por defecto; en la segunda, tomará
el valor que se le pasa (5).

Devolviendo un 2;

Devolviendo un 6;

Lista de argumentos de longitud variable


Una de las características más importantes en el
manejo de funciones de PHP4 es la posibilidad de
tener listas de argumentos de longitud variable. Eso
implica que una función no tiene por qué tener
siempre el mismo número de parámetros en cada
llamada. Además, no se requiere sintaxis adicional.
Para el manejo de esta característica se utilizarán
tres funciones:
• func_num_args(): devuelve el número de
argumentos pasado a una función. Su llamada se
lleva a cabo desde dentro de la propia función.
• func_get_args(): devuelve un vector con los
valores pasados a la función como argumentos.
• func_get_arg(entero): devuelve el n-ésimo
componente de la lista de argumentos.
function imprime_lista() {

$num = func_num_args();

echo "Lista con la función func_get_arg() <BR>";

for ($i = 0; $i < $num; $i++)

echo "Parámetro $i:".func_get_arg($i)."<BR>";

echo "Lista con la función func_get_args()";

$lista = func_get_args();

for ($i = 0; $i < $num; $i++)

echo "Parámetro $i: ".lista[$i]."<BR>";

Funciones variables
Si ponemos los paréntesis tras el nombre de una
variable, PHP tratará de ejecutar una función con el
mismo nombre que el valor almacenado por la variable.
Viene a ser como sustituir la variable por su valor.
En el siguiente ejemplo, vemos como podemos invocar a
la función del ejemplo anterior por medio de este
curioso método.

$nombre_funcion = "imprime_lista";

$nombre_funcion(); /* ejecuta la función */

$nombre_funcion(1,3,4,5,1,"h");

/* ejecuta la función pasándole varios argumentos */


Clases y Objetos
Para poder seguir este apartado es necesario que el
lector tenga nociones de lo que es la programación
orientada a objetos, ya que esto solo pretende ser un
libro de PHP, y no un libro de programación.
Una clase es un conjunto de variables al que se
asocia una serie de funciones. Se utiliza la
sintaxis:

class nombre {

var variables;

declaración y definición de funciones;

Lo veremos más claro con un ejemplo. Vamos a crear un


objeto que represente un vector:

class vector {

var $x, $y; /* componentes */

function vector($cx = 0, $cy = 0) {

/* constructor */

$this->x = $cx;

/* con "this" nos referimos a este objeto */

$this->y = $cy;

function imprime() {

print("($this->x, $this->y)");

}
function pone_x($cx) {

$this->x = $cx;

function pone_y($cy) {

$this->y = $cy;

En el ejemplo tenemos dos variables y cuatro


funciones. La primera de ellas es el constructor
(tiene el mismo nombre que el objeto) y observamos
que tiene valor por defecto para sus parámetros.
Vamos a ver a continuación como utilizar esta clase.

$v = new vector;

/* inicializa la clase con los valores por defecto */

$v->imprime();

$v->pone_x(5);

$v->imprime();

La salida del fragmento de código anterior sería:

(0,0)(5,0)

En PHP también existe la herencia. Se lleva a cabo


mediante la palabra reservada extends, e implica que
todos los atributos y funciones de la clase base los
tendrá también la clase heredera. Vamos a añadirle un
punto de origen a la clase anterior.

class vector_extendido extends vector {

var $ox, $oy;

function vector_extendido($cx = 0, $cy = 0, $ox = 0, $oy =


0)

$this->x = $cx;
$this->y = $cy;

$this->ox = $ox;

$this->oy = $oy;

function imprime_o() {

print("($this->ox, $this->oy)");

function pone_ox($ox) {

$this->ox = $ox;

function pone_oy($oy) {

$this->oy = $oy;

Un posible código que utilice la nueva clase podría


ser:

$v = new vector_extendido(1,2,0,0);

$v->pone_x(5);

$v->pone_ox(2);

$v->imprime();

$v->imprime_o();

Y su salida sería:

(5,2)(2,0)
Algunas funciones de PHP
En este capítulo no van a incluirse todas y cada una
de las funciones que soportan PHP y sus módulos. Se
trata de crear una guía general de las funciones más
comunes del lenguaje.

Funciones básicas
echo y print

A lo largo de todo el texto hemos utilizado las


funciones echo y print, sin preocuparnos de explicar
su significado y sintaxis. Dichas funciones, como ya
habrá supuesto el lector, imprimen en la salida
estándar el argumento que se les pasa. Hay que hacer
notar que su sintaxis es muy flexible, siendo válidas
las cuatro siguientes llamadas:

$valor = 52;

echo "El valor es $valor";

echo ("El valor es $valor");

print "El valor es $valor";

print ("El valor es $valor");

Funciones de vectores
Ya hemos visto con anterioridad el manejo de vectores
así como su inicialización. En este apartado vamos a
estudiar una serie de funciones que van a simplificar
el manejo de vectores. Además, prestaremos especial
atención a una función de inicialización que aparece
en esta sección.

array()

Esta función devuelve un vector que forma en base a


los parámetros que se le pasan. A los parámetros se
les puede dar un índice asociativo por medio del
símbolo =>. Vamos a ver un ejemplo donde creamos un
vector de dos dimensiones:

$varios = array( "colores" => array("a"=>"amarillo,


"b"=>"rojo"), "sentidos" => array("tacto", "gusto",
"olfato", "oido", "vista")

);

El resto de funciones

Nombre parámetros descripción

Devuelve un vector usando


los valores del vector de
array_count_values() vector A entrada como índices
asociativos y su frecuencia
como valor

Devuelve el vector inverso


array_flip() vector A
a A

Devuelve un vector con


array_keys() vector A todos los valores de clave
de A

Devuelve el vector
vectores A;
array_merge() concatenación de los
B, ...
vectores A, B, ...

Devuelve y extrae el valor


array_pop() vector V
del último elemento de V

Añade al final de V los


vector V;
elementos A, B, ...
array_push gen A, B,
devolviendo el número de
...
elementos añadidos

Devuelve un vector con los


array_reverse() vector V elementos de V en orden
inverso
Devuelve y extrae el valor
array_shift vector V
del primer elemento de V

Extrae de un vector los


elementos que van desde la
vector V;
array_slice() posición A hasta A+B,
ent A, B
devolviendo un nuevo vector
con dichos elementos

Elimina los elementos de V


vector V;
desde A hasta A+B e inserta
array_splice() ent A, B;
en su lugar los elementos
vector W
del vector W

Inserta los elementos A, B,


vector V;
... al principio de V y
array_unshift() gen A, B,
devuelve la cantidad de
...
elementos insertados

Devuelve un vector con


array_values() vector V
todos los valores de V

Aplica la función f a todos


vector V;
los elementos del vector V
array_walk() cad f; gen
añadiendo los parámetros A,
A, B, ...
B, ... en cada llamada

Ordena un vector de forma


arsort() vector V inversa manteniendo la
asociación clave-valor

Ordena un vector
asort() vector V manteniendo la asociación
clave-valor

Devuelve el número de
count() gen V
elementos de una variable

Devuelve el elemento actual


current() vector V
de V

Devuelve la próxima pareja


each() vector V
clave-valor de V
Lleva el puntero interno
end() vector V del vector a su último
elemento

Devuelve verdadero si A se
gen A;
in_array() encuentra en V; Falso en
vector V
otro caso

Ordena V de forma inversa


krsort() vector V
en base a la clave

ksort() vector V Ordena V en base a la clave

Asigna variables como si


list() gen A, B...
fueran un vector

Avanza el puntero interno


next() vector V de un vector a la siguinte
posición

Devuelve el elemento
pos() vector V señalado por el puntero
interno de V

Retrocede el puntero
prev() vector V interno de un vector V a la
posición anterior

Coloca el puntero interno


reset() vector V
de V en la primera posición

rsort() vector V Ordena V de forma inversa

"Desordena" los elementos


shuffle() vector V
de V

Devuelve el número de
Sizeof() vector V
elementos de V

Sort vector V Ordena el vector V

Ordena un vector en base a


uasort() vector V una función de comparación
definida por el usuario y
manteniendo la asociación
clave-valor

Ordena un vector en base a


una función de comparación
uksort() vector V
definida por el usuario
sobre las claves

Ordena un vector en base a


una función de comparación
usort() vector V
definida por el usuario
sobre los valores

Funciones de cadenas
PHP ofrece multitud de funciones para el manejo de
cadenas. En esta sección mostramos sólo las más
utilizadas.

nombre parámetros descripción

print() cad A Imprime A en la salida estándar

Imprime A en la salida estándar


printf() cad A aplicándole un formato (estilo
C)

Devuelve una cadena formateada


cad F; gen
sprint() según F con las variable A, B,
A, B, ...
...

Busca B dentro de A y devuelve


strstr() /
cad A, B desde su primera aparición
strchr()
hasta el final de A.

Devuelve un valor menor que 0


si A es menor que B; mayor que
strcmp cad A, B
1 si B es menor que A; y 0 si
son idénticas

Como strstr pero insensible a


stristr() cad A, B
mayúsculas/minúsculas
strlen() cad A Devuelve la longitud de A

Devuelve la posición de la
strpos() cad A, B
primera aparición de B en A

Análoga a strchr() pero


strrchr() cad A, B empezando por el final de la
cadena

strrev() cad A Invierte una cadena

Análoga a strpos() pero


strrpos() cad A, B empezando por el final de la
cadena

Análoga a strchr() pero


strrchr() cad A, B empezando por el final de la
cadena

Extrae un token de A en base a


strtok() cad A, B
B

strtolower() / Pone una cadena en minúsculas /


cad A
strtoupper() mayúsculas

Reemplaza por B las apariciones


str_replace() cad A, B, C
de A en C

Devuelve la subcadena de A que


cad A; ent
substr() va desde la posición B hasta la
B, C
B+C.

Sustituye en A la subcadena
cad A, B;
substr_replace() desde C hasta C+D por la
ent C, D
subcadena B

Funciones de variables

nombre parámetros descripción

doubleval() esc A Devuelve el valor double de A


Devuelve falso si la variable
empty() gen A
tiene valor vacío o cero

Devuelve el tipo de la variable


en forma de cadena: "integer",
gettipo() gen A
"double", "string", "array",
"object", "unknown tipo"

Devuelve el valor entero de A


est A; ent
intval() usando B como base numérica. Por
B
defecto, B = 10

Devuelve verdadero si A es un
is_array() gen A
vector.

is_float() /
Devuelve verdadero si A es un
is_double() / gen A
real.
is_real()

is_int() /
Devuelve verdadero si A es un
is_integer() / gen A
entero.
is_long()

Devuelve verdadero si A es una


is_string() gen A
cadena.

Devuelve verdadero si A es un
is_object() gen A
objeto.

isset() gen A Devuelve verdadero si existe A

gen A; cad
settipo() Fuerza la variable A al tipo B
B

Convierte el valor de A a una


strval() gen A
cadena

unset() gen A Destruye A


Programación WEB
En este apartado vamos a tratar la instalación y
configuración de un servidor web, utilizando Apache
como servidor, PHP como lenguaje interpretado de alto
nivel y PostgreSQL como base de datos. Con esta
combinacion podremos crear páginas dinamicas y
obtener informacion de nuestra base de datos para
presentarla via web. Por ultimo, daremos unos cuantos
ejemplos de como programar nuestras páginas web
utilizando estos programas.

Cuenta de administración de la BD
[localhost]$ su

[localhost]$ /usr/sbin/adduser postgres

[localhost]$ passwd postgres

[localhost]$ exit

Una vez creada la cuenta Postgres crearemos los


directorios que utilizaremos para instalar PostgreSQL
con los permisos adecuados:
Una vez que hemos terminado de instalar la base de
datos y configurar nuestro sistema, tenemos que
inicializarla y arrancarla:

Inicialización y arranque de la BD
[localhost]$ su postgres

[localhost]$ initdb

[localhost]$ cd

[localhost]$ nohup postmaster -i > pgserver.log 2>&1 &


[localhost]$ exit

Ya tenemos nuestra base de datos PostgreSQL,


instalada y funcionando. Ahora solo tenemos que
administrarla, para ello nada mejor que leerse los
manuales de documentación de la misma y aprender SQL.
Solamente nos queda hacer un par de ajustes en la
configuración para que podamos acceder a postgreSQL
via PHP/web. Lo primero es incluir en el archivo
/usr/local/pgsql/data/pg_hba.conf la siguiente linea:
host all tu_maquina_IP tu_maquina_NETMASK trust
Y la segunda es dar privilegios de acceso en tu base
de datos/tablas al usuario "Nobody"para que pueda
coger los datos de la misma (Nobody es el usuario que
ejecuta el servidor Apache por defecto). Para ello
puedes hacer lo siguiente:

Dando privilegios a la BD
Suponemos que tenemos una base de datos llamada
prueba, con una tabla llamada direcciones

[localhost]$ su postgres

[localhost]$ psql prueba

prueba=> GRANT SELECT ON direcciones

prueba=> TO nobody;

prueba=> \z

prueba=> \q

[localhost]$ exit

Arranque del servidor


[localhost]$ su
[localhost]$ /usr/local/apache/bin/httpd -f /usr/local/ apache/ conf/
httpd.conf

[localhost]$ exit

NOTA: Para obtener toda la información/documentación


completa pasaros por Apache documentación y PHP
documentación.

Ejemplos
Lo primero que tenemos que hacer es comprobar que PHP
funciona bien. Para ello podemos crear un archivo
index.php en nuestro catalogo web /home/httpd/html/
con las siguientes lineas:

Creación archivo index.php


<HTML>

<HEAD>

<TITLE>Pagina index de prueba</TITLE>

</HEAD>

<BODY>

<?php

/* Codigo php de esta pagina */

echo "Esto es una prueba<BR>

Dia/hora: ".date("d/m/Y - H:i:s")."<BR>";

?>
</BODY>

</HTML>

Este archivo debería de daros como resultado dos


líneas en pantalla, una de ellas con el día y la hora
de vuestro servidor. Una vez comprobado que PHP
funciona, vamos a crear una página web, que acceda
mediante PHP a PostgreSQL y que nos devuelva como
resultado el contenido de una de las tablas de la
base de datos. Suponemos que ya tenemos una base de
datos llamada prueba, con una tabla direcciones que
contiene tres campos calle, ciudad, pais. La máquina
que estamos utilizando es servidor.domain.es y
PostgreSQL utiliza el puerto 5432(puerto por
defecto).

Acceso a PostgreSQL
<HTML>

<HEAD>

<TITLE>Pagina index de prueba</TITLE>

</HEAD>

<BODY>

<?php

/* Conexión a PostgreSQL */

/* Conexión a la base de datos */

$conexion = pg_pconnect("host=servidor.domain.es

port=5432
dbname=prueba");

if (!$conexion) {

echo "<CENTER>
Problemas de conexión con la base de
datos.

</CENTER>";

exit;

$sql="SELECT * FROM direcciones ORDER BY pais;";

/* Ejecuta y almacena el resultado de

SQL en $resultado_set */

$resultado_set = pg_Exec ($conexion, $sql);

$filas = pg_NumRows($resultado_set);

/* Presenta la informacion almacenada en


$resultado_set */

for ($j=0; $j < $filas; $j++) {

echo "Direccion: ".pg_result($resultado_set, $j,


0)." <BR>

Ciudad: ".pg_result($resultado_set, $j, 1)."


<BR>

Pais: ".pg_result($resultado_set, $j, 2)."


<P>";

/* Cierra la conexion con la base de datos */

pg_close($conexion);

?>

</BODY>

</HTML>
Esta página web debería de presentar la información
contenida en la tabla direcciones de la base de datos
prueba. Y a partir de aquí solamente tienes que leer
la documentación y usar vuestra imaginación para
crear páginas web dinámicas, actualizadas y que
presenten la información contenida en vuestras bases
de datos.
A continuación presentamos un formulario PHP que
recibe datos desde el teclado hacia la tabla de la
base de datos:

<html>

<head>

<title>Cómo introducir datos en un formulario</title>

</head>

<form action="form_results.php3" method="GET">

<p>Nombre: <input type="text" name="first_name">

<br>Apellidos: <input type="text" name="last_name">

<br>Dirección: <input type="text" name="address">

<br>Ciudad: <input type="text" name="city">

<br>Estado: <input type="text" name="state">

<br>Código Postal: <input type="text" name="zip">

<br>Teléfono: <input type="text" name="home_phone">

<p><input type="submit" name="Enviar">

<p><input type="reset" name="Restablecer">


</form>

</body>

</html >

Mientras que el archivo form_results.php3 tiene el


siguiente código para mostrar el resultado:

<html>

<head>

<title>Resultados del formulario</title>

</head>

<body>

<h2>Abajo están los resultados del envío del formulario</h2>

<?php

print("<p>Nombre: <b>$first_name</b>\n");

print("<br>Apellidos: <b>$last_name</b>\n");

print("<br>Dirección: <b>$address</b>\n");

print("<br>Ciudad: <b>$city</b>\n");

print("<br>Estado: <b>$state</b>\n");

print("<br>Código Postal: <b>$zip</b>\n");

print("<br>Teléfono: <b>$home_phone</b>\n");
?>

</body>

</html>

El formulario debería ver de la forma siguiente:

el siguiente código muestra cómo trabajar con las


cookies:

<?php

if(isset($f_name)):

/* restablecer la cookie si había una antes */


setcookie("f_name",$f_name, time() + 365 * 86400);

/* establecer la fecha antigua a la fecha de la cookie */

if(isset($date)):

$old_date = $date;

else:

$old_date = date("l F j, Y");

endif;

$date = date("l F j, Y");

/* restablecer la fecha de la cookie a hoy */

setcookie("date", $date, time() + 365 * 86400);

endif;

?>

<html>

<head>

<title>El script Cookie</title>

</head>

<body>

<?php

if(isset($f_name)):
?>

<h3>Bienvenido otra vez a la página <?php print $f_name ?>!

<br>La última vez que nos visitó fue <?php print $old_date ?>.</h3>

<?php

else:

?>

<form action="cookie.php3" method="POST">

Indíquenos su nombre: <input type="text" name="f_name">

<input type="submit" name="submit" value="Enviar">

</form>

<?

endif;

?>

</body>

</html>

la forma de incluir archivos con funciones es la


siguiente:

<?

include("head.inc");
head("$title");

?>

<table width="600">

<tr><td>

<? headline($headline); ?>

<!-- Nav Links -->

<? include("navlinks.inc") ?>

<table>

<tr><td width="300">

<!-- CONTENIDO aquí -->

<? print $content ?>

</td>

<td valign="TOP">

<!-- Búsqueda, mapa del Site, link a Archivos -->

<? include("search.inc") ?>

</tr></td>

</table>

</td></tr>

<tr><td>
<!—El pié de página aquí -->

<? include("footer.inc") ?>

</tr></td>

</table>

</body>

</html>

mientras que el archivo Head.inc contiene el código


PHP:

<?

function head($title) {

?>

<html>

<head>

<title><? print $title ?></title>

<!—El estilo aquí -->

<? include("style.inc") ?>

</head>

<body background="images/stitch_bg.gif">

<div align="RIGHT"><? print date("F jS, Y") ?></div>


<img src="images/stitch_logo.gif" width=516 height=122>

<p>

<?

function headline($headline) {

?>

<h1><i><? print $headline ?></i></h1>

<?

?>

estos programas son ilustrativos de cómo incluir,


ocultar, funciones o reglas del negocio en las
aplicaciones WEB.
Manejo de errores
PHP nos ofrece una serie de facilidades para el
manejo de errores. Encontramos cuatro clases de
errores y avisos:
• Errores de funciones (1)
• Avisos corrientes (2)
• Errores sintácticos (4)
• Notificación (que pueden ignorarse) (8)
Además, como otros muchos aspectos de PHP, el nivel
de notificación de errores es configurable. Para ello
contamos con las tres opciones siguientes:
• Cambiarlo en el archivo de configuración de php
(php3.ini o php.ini).
• Configurarlo en el archivo de configuración de
Apache (httpd.conf).
• Utilizar la función error_reporting(). Esta
función, devuelve fija el nivel de error.
ACERCA DEL AUTOR
Dr. Jorge Domínguez Chávez, mexicano, es
administrador de base de datos Oracle 8i, programador
de aplicaciones WEB en Visual Basic, Java y PHP. Su
área de investigación incluye el diseño, la
implementación y teoría de base de datos, la
algorítmica computacional y el desarrollo de
aplicaciones WEB en la arquitectura CORBA y EJB. Es
profesor del Programa de Postgrado en Sistemas del
Instituto Universitario de Tecnología La Victoria,
Venezuela y profesor - administrador de sistemas en
la Universidad Central de Venezuela, Facultades de
Ciencias Veterinarias y de Agronomía.
El Prof. Domínguez ha publicado varios libros sobre
programación y diseño de base de datos, con
frecuencia publica artículos en las prestigiosas
revistas GACETADELINUX y GAZETADULINUX y participa
activamente en congresos sobre la divulgación y
aplicación del software libre. Tiene más de 22 años
de experiencia programando sistemas de información y
aplicaciones WEB, PHP, ASP y JSP.
Actualmente tiene un fuerte interés en los aspectos
filosóficos de la computabilidad y es un ferviente
propulsor de TUX.
Puede comunicarse con el Prof. Domínguez a través de
su correo electrónico jodocha@mexico.com
Observaciones
El presente material editorial está desarrollado en
un computador ejecutando Linux Mandrake 8.0, en la
suite StarOffice 5.0 y transformando a PDF con el
mismo StarOffice 5.0. Los cuales pueden se utilizados
según licencia GNU.

También podría gustarte