Verificando si un string es un número válido

2 comentarios

A veces podemos necesitar saber si en una columna de tipo CHAR o VARCHAR se ha guardado un número válido.

¿Cómo lo conseguimos rápidamente?

Usando el predicado de comparación SIMILAR TO.

Ejemplo 1:

En la tabla de ALUMNOS tenemos una columna donde guardamos el número de la matrícula. Queremos verificar que solamente haya números válidos allí.

NUMEROS1

Captura 1. Si haces clic en la imagen la verás más grande

Listado 1.

SELECT
   ALU_MATRIC,
   ALU_NOMBRE
FROM
   ALUMNOS
WHERE
   ALU_MATRIC SIMILAR TO '[0-9]*'

NUMEROS2

Captura 2. Si haces clic en la imagen la verás más grande

Ejemplo 2:

Queremos saber cuales son los alumnos cuyo número de matrícula es incorrecto. Para ello solamente agregamos NOT, como podemos ver en el Listado 2.

Listado 2.

SELECT
   ALU_MATRIC,
   ALU_NOMBRE
FROM
   ALUMNOS
WHERE
   ALU_MATRIC NOT SIMILAR TO '[0-9]*'

NUMEROS3

Captura 3. Si haces clic en la imagen la verás más grande

Si lo que nos interesa es saber cuantos alumnos tienen número de matrícula correcto o número de matrícula incorrecto entonces en lugar de seleccionar la Matrícula y el Nombre usaríamos la función agregada COUNT().

Ejemplo 3:

En la tabla FACTURAS tenemos datos de las Facturas de venta.

NUMEROS4

Captura 4. Si haces clic en la imagen la verás más grande

Por alguna razón que no sabemos, la columna FAC_COBRAD es de  tipo VARCHAR, no es numérica. Por eso ahora queremos verificar que todos los números allí escritos sean números válidos.

Listado 3.

SELECT
   FAC_NUMERO,
   FAC_FECVEN,
   FAC_COBRAD
FROM
   FACTURAS
WHERE
   TRIM(FAC_COBRAD) SIMILAR TO '[\+\-]?[0-9]*.?[0-9]*' ESCAPE '\'

NUMEROS5

Captura 5. Si haces clic en la imagen la verás más grande

Explicando el patrón usado en SIMILAR TO

NUMEROS6

Conclusión:

Saber si en una columna de tipo CHAR o VARCHAR hay solamente números válidos puede ser muy útil muchas veces, en este artículo se mostró una técnica para realizar esa verificación.

Artículos relacionados:

Los predicados de comparación

Usando SIMILAR TO

El índice del blog Firebird21

El foro del blog Firebird21

 

Anuncios

Conectando a una Base de Datos desde dos servidores

2 comentarios

Si tu intención es corromper, destruir, destrozar internamente, a una Base de Datos del Firebird, esta es una de las formas más seguras de conseguirlo.

Así que si tu intención es mantener a la Base de Datos en muy buen estado de conservación, jamás hagas algo así.

Veamos un poco más detalladamente lo que sucedería si intentas conectarte a una misma Base de Datos desde dos (o más) servidores de Firebird al mismo tiempo.

Como recordarás, Firebird tiene tres arquitecturas: Classic, SuperClassic, SuperServer.

Classic y SuperClassic son ejecutados a través de un programa que se llama FB_INET_SERVER.EXE y realizan los bloqueos correctos usando el proceso FB_LOCK_MGR.

Entonces, aunque cada conexión usando Classic y SuperClassic se hace mediante servidores distintos (cada instancia ejecuta a un FB_INET_SERVER.EXE), el proceso FB_LOCK_MGR se encarga de gestionar esas conexiones asegurándose de que la Base de Datos no se corrompa.

SuperServer por otro lado es ejecutado a través de un programa que se llama FBSERVER.EXE, el cual requiere acceso exclusivo a la Base de Datos así que resulta imposible que otro Servidor pueda acceder a una Base de Datos a la cual alguien ya se conectó usando SuperServer. Y también que si alguien ya se conectó a una Base de Datos (usando Classic, SuperClassic, o SuperServer)  alguien más pueda conectarse a ella usando otro Servidor SuperServer.

Entonces la situación es la siguiente:

  • Si alguien se conectó a una Base de Datos usando SuperServer, nadie más podrá conectarse a esa Base de Datos usando otro Servidor (sea Classic, SuperClassic, o SuperServer dicho Servidor).
  • Si alguien se conectó a una Base de Datos (usando Classic, SuperClassic, o SuperServer), conectarse a esa Base de Datos usando otro Servidor, que sea SuperServer, será imposible.
  • Si alguien se conectó a una Base de Datos usando Classic o SuperClassic conectarse a esa Base de Datos usando otro Servidor que sea Classic o SuperClassic, sí es posible … pero corromperá a la Base de Datos.

El último punto es el peligroso.

¿Y por qué se corromperá la Base de Datos?

Hay dos cosas que pueden salir mal, muy mal.

Primero. Los índices estarán corruptos. Si desde el Servidor 1 se están insertando filas a una tabla y desde el Servidor 2 también se están insertando filas a esa misma tabla, cada Servidor actualizará los índices de la tabla, al mismo tiempo, y eso ocasionará un desbarajuste mayúsculo. Esto es solucionable. Cuando ningún Servidor está insertando ni borrando ni modificando filas de esa tabla se reconstruyen los índices y asunto solucionado. Pero si se trata de una tabla que tiene muchos movimientos tantas reconstrucciones de índices afectarán gravemente al rendimiento y además mientras tanto las consultas mostrarán datos incorrectos y los procesos también realizarán actualizaciones incorrectas.

Segundo. Un Servidor sobre-escribe las páginas del otro Servidor. Esto es gravísimo porque causará corrupción en el 100% de los casos. Como recordarás, Firebird guarda todo, absolutamente todo lo que se hace en una Base de Datos en bloques de bytes llamados páginas. Si hay dos servidores accediendo a una misma Base de Datos, no se tendrá una buena administración de esas páginas porque cada Servidor llamará a un proceso FB_LOCK_MGR diferente. Y cada uno de esos procesos guarda el estado actual de las páginas de la Base de Datos en una porción de memoria RAM a la cual tiene acceso exclusivo.Por ejemplo: el Servidor 1 necesita una nueva página para datos de la tabla VENTAS, ve que la página número 123456 está libre y marca esa página para contener datos de la tabla VENTAS. Todo bien hasta ahí. Pero el Servidor 2 también necesita una nueva página, en este caso para guardar los índices de la tabla PRODUCTOS, ve que la página número 123456 está libre y marca a esa página para contener los índices de la tabla PRODUCTOS. ¿Consecuencia? Que las ventas que se guardaron en la página 123456 se perdieron totalmente. Y que los índices que correspondían a esas ventas están huérfanos. Los productos no tendrán problemas, pero las ventas se corrompieron. Por supuesto que más adelante la situación puede ser a la inversa y los datos corruptos son los correspondientes a la tabla de PRODUCTOS. El problema por lo tanto es que si dos o más servidores están conectados a una misma Base de Datos la correcta administración de las páginas de esa Base de Datos será imposible, y la corrupción no tardará en llegar.

Conclusión:

Conectarse a una misma Base de Datos usando dos servidores, siendo al menos uno de esos servidores SuperServer, es imposible.

Si los servidores son Classic o SuperClassic la conexión sí es posible, pero con toda seguridad causará corrupción a la Base de Datos.

¿Por qué se corromperá la Base de Datos?

Un problema se tendrá con los índices, y aunque esto es solucionable será muy molesto porque se tendrá que reconstruirlos constantemente y eso hará que todas las operaciones se vuelvan muy lentas, o que las consultas muestren datos inconsistentes.

Pero el problema realmente grave se tendrá con las páginas de datos que fueron escritas por uno de los servidores y sobre-escritas por el otro Servidor. Esto ocurrirá porque cada Servidor guarda en una porción de la memoria RAM  (que es de su uso exclusivo) el estado de las páginas de la Base de Datos. Por lo tanto, lo que hace un Servidor el otro Servidor no lo sabe, lo ignora completamente.

Y claro, un Servidor marca una página para que guarde algo, y el otro Servidor marca a esa misma página para que guarde otra cosa. ¿Consecuencia? Corrupción asegurada, 100% asegurada.

Artículos relacionados:

Entendiendo las páginas de la Base de Datos

Eligiendo el tamaño adecuado de las páginas de la Base de Datos

El índice del blog Firebird21

El foro del blog Firebird21

 

 

 

Haciendo backups con GBAK (3)

Deja un comentario

En artículos anteriores ya vimos mucho sobre la parte teórica de hacer backups en general, y de hacer backups con GBAK en particular, así que vayamos ahora a la parte práctica:

La sintaxis general de GBAK

GBAK <opciones> -USER <usuario> -PASSWORD <contraseña> <origen> <destino>

Opciones: con ellas le indicamos a GBAK lo que queremos hacer

Usuario: es el nombre del usuario que realizará el backup o la restauración. Debe ser SYSDBA o el creador de la Base de Datos o un usuario con el rol RDB$ADMIN.

Contraseña: es la contraseña del usuario que realizará el backup o la restauración

Origen: si se hará un backup es nombre completo de la Base de Datos (la cual generalmente tiene la extensión .FDB) o un alias definido en el archivo ALIASES.CONF; si se hará una restauración es el nombre completo del archivo de backup (el cual generalmente tiene la extensión .FBK)

Destino: si se hará un backup, es el nombre del archivo que será generado (generalmente con la extensión .FBK); si se hará una restauración es el nombre que tendrá la nueva Base de Datos (generalmente con la extensión .FDB)

Tipos de opciones

Las opciones de GBAK pueden ser de tres tipos:

  1. Opciones generales
  2. Opciones del backup
  3. Opciones de la restauración

Las opciones generales son las que se pueden usar en ambos casos (es decir, cuando se hace el backup o cuando se hace la restauración) y son las siguientes:

nodbtriggers Impide que se desencadenen los triggers de la Base de Datos.

-pas[sword] <contraseña> La contraseña que el usuario utiliza para conectarse a la Base de Datos. Alternativamente se puede usar -fet[ch_password] para no escribir la contraseña

-fet[ch_password] <nombre_archivo> En lugar de escribir la contraseña en la línea de comandos se puede extraerla de un archivo, de esta manera si hay un curioso mirando lo que escribes no podrá saber cual es tu contraseña

-m[etadata] Solamente hace el backup o la restauración de los metados, los datos introducidos por los usuarios serán ignorados

-role <nombre_del_rol> Si el usuario usó un rol

-se[rvice] <hostname[/port]>:service_mgr Cuando se está haciendo el backup: el archivo se crea en el Servidor, usando para ello al Service Manager. Cuando se está haciendo la restauración: la Base de Datos se crea en el Servidor, usando para ello el Service Manager. Tanto la Base de Datos como el archivo de backup como el archivo de log deben encontrarse en uno de los discos duros del Servidor y cuando se los especifica debe hacerse desde la perspectiva del Servidor, aún cuando se llame a GBAK desde una computadora remota. La ventaja de usar el Service Manager es que el backup y la restauración se hacen muy rápido.

-u[ser] Nombre del usuario de la Base de Datos

-v[erbose] Muestra en la pantalla lo que está haciendo GBAK

-y [nombre_archivo> Crea un archivo de log y escribe todos los mensajes de GBAK que normalmente se mostrarían en la pantalla en el archivo de log. El archivo de log no debe existir. Por lo tanto, si existe, hay que borrarlo antes de ejecutar a GBAK. Si se usa la opción -se[rvice] el archivo de log debe encontrarse sí o sí en uno de los discos duros del Servidor.

-y suppress No muestra los mensajes de GBAK en la pantalla ni los escribe en un archivo de log

-z Muestra la versión de GBAK y del Servidor del Firebird

Las opciones del backup son las que solamente se usan cuando se está realizando el backup y son las siguientes:

-b[ackup_database] Se desea realizar un backup. Es opcional.

-co[nvert] Convierte las tablas externas en tablas internas

-e[xpand] Crea un archivo de backup no comprimido. Por lo tanto, ocupará más espacio en el disco duro

-fa[ctor] n Factor de bloqueo para los dispositivos de cinta

-g[arbage_collect] No recolecta la basura durante el backup, por lo tanto el backup terminará más rápido. Debes usar esta opción cuando inmediatamente después del backup harás un Sweep

-ig[nore] Ignora los checksums mientras realiza el backup

-l[imbo] Ignora las transacciones en limbo mientras realiza el backup

-nt Formato no transportable. Debes usarlo solamente cuando estás 100% seguro que la restauración se hará en el mismo Sistema Operativo y con la misma versión del Firebird

-t[ransportable] Crea un backup que es transportable entre Sistemas Operativos y versiones del Firebird. Es la opción por defecto

Las opciones de la restauración son las que solamente se usan cuando se está restaurando el backup y son las siguientes:

-bu[ffers] Establece el tamaño del caché para la Base de Datos restaurada

-c[reate_database] Restaura a una nueva Base de Datos (el nombre de la nueva Base de Datos no debe existir)

-fix_fss_d[ata] Repara códigos UNICODE_FSS de los datos que estaban mal formados

-fix_fss_m[etadata] Repara códigos UNICODE_FSS de los metadatos que estaban mal formados

-i[nactive] Todos los índices son restaurados como inactivos

-k[ill] No crea los archivos de shadow que fueron definidos en el backup

-mo[de] read_write La Base de Datos restaurada será read/write (o sea: lectura y escritura). Es el valor por defecto

-mo[de] read_only La Base de Datos restaurada solamente podrá ser leída y no se podrán realizar cambios en ella

-n[o_validity] No restaura las restricciones de validación. Sirve para restaurar datos que no cumplen con las restricciones y que no podrían ser restaurados de otra manera

-o[ne_at_a_time] Lo normal es que la restauración se haga en una sola transacción para toda la Base de Datos. El problema es que si se encuentra algún error entonces ninguna tabla será restaurada. Con esta opción se abre una transacción para cada tabla y después de restaurarla se ejecuta el COMMIT. De esta manera se puede restaurar parcialmente una Base de Datos corrupta.

-p[age_size] <tamaño_página> Pone el tamaño de la página de la Base de Datos. Los valores posibles son 4096, 8192, 16384. El valor por defecto es 4096.

-r[eplace_database] Restaura sobre una Base de Datos existente. Esto solamente puede ser realizado por SYSDBA o por el creador de la Base de Datos que será sobre-escrita. ¡¡¡CUIDADO!!! No hay que restaurar sobre una Base de Datos que está siendo usada.

-rep[lace_database] Igual que la opción anterior. Es simplemente una nueva abreviación.

-r[ecreate_database] o[verwrite] Restaura sobre una Base de Datos existente. Solamente puede ser ejecutada por SYSDBA o por el creador de la Base de Datos que será sobre-escrita. Si la Base de Datos está siendo usada el Firebird te lo advertirá porque es algo que no deberías hacer, de todas maneras: debes ser cuidadoso

-use_[all_space] Normalmente cuando se realiza la restauración cada página de la Base de Datos es escrita en un 80%, en cambio si se usa esta opción cada página será llenada al 100%. Es muy útil cuando la Base de Datos restaurada será read-only (y por lo tanto ya no será modificada), porque ahorra mucho espacio en el disco duro.

Ejemplo Nº 1. Un backup normal

GBAK -v -USER SYSDBA -PASSWORD masterkey D:\BASESDATOS\CONTA.FDB D:\BACKUPS\CONTA.FBK

Si en el archivo ALIASES.CONF hemos definido CONTA=D:\BASEDATOS\CONTA.FDB también se podría escribir:

GBAK -v -USER SYSDBA -PASSWORD masterkey CONTA D:\BACKUPS\CONTA.FBK

La opción -v nos muestra en la pantalla lo que está haciendo GBAK.

Ejemplo Nº 2. Un backup con archivo de log

DEL D:\BACKUPS\CONTA.LOG

GBAK -v -USER SYSDBA -PASSWORD masterkey -y D:\BACKUPS\CONTA.LOG D:\BASESDATOS\CONTA.FDB D:\BACKUPS\CONTA.FBK

El archivo de log no debe existir, por eso se lo borra primero. Todo lo que GBAK hubiera mostrado en la pantalla será escrito en el archivo de log, así ese archivo podrá ser revisado cuando se lo necesite.

Ejemplo Nº 3. Una restauración normal

GBAK -c -v -USER SYSDBA -PASSWORD masterkey D:\BACKUPS\CONTA.FBK D:\BASESDATOS\CONTA2.FDB

La Base de Datos restaurada tiene un nombre distinto (CONTA2.FDB) al de la Base de Datos original (CONTA.FDB), esa es la práctica normal y la más recomendada.

Ejemplo Nº 4. Restaurando a una Base de Datos de sólo lectura

GBAK -c -v -mode read_only -use_all_space -USER SYSDBA -PASSWORD masterkey D:\BACKUPS\CONTA.FBK D:\BASESDATOS\CONTA3.FDB

Como -mode es read_only entonces la Base de Datos será de sólo lectura, y como será de sólo lectura entonces se establece que cada página esté llena al 100%, ahorrando así mucho espacio en el disco duro.

Ejemplo Nº 5. Backup y restauración al mismo tiempo

Es muy útil para verificar que el archivo de backup se encuentra en perfecto estado. Un archivo de backup que no se encuentra en perfecto estado puede ser inútil e inservible, por lo tanto una muy buena medida administrativa es verificar que está ok.

GBAK -c [opciones] <base_de_datos_origen> stdout | GBAK -r [opciones] stdin <base_de_datos_destino>

Como puedes ver aquí el programa GBAK se ejecuta dos veces:

  • La primera vez, en lugar de crear al backup en el disco duro (que es lo normal) lo crea en stdout que es la salida estándar (la cual por defecto es la pantalla de la computadora)
  • La barra vertical significa que el programa que se encuentre a continuación (y que en este caso también es GBAK pero podría ser otro) tome como entrada la salida del primer programa ¿en qué lugar? en donde se encuentre stdin
  • El primer GBAK crea el archivo de backup, el segundo GBAK restaura ese archivo de backup

Esta es una forma simplificada de realizar un ciclo backup/restore normal:

  • Con GBAK conectarse a una Base de Datos y crear un archivo de backup
  • Con GBAK leer el archivo de backup y crear una nueva Base de Datos

Artículos relacionados:

Entendiendo a las bases de datos

Entendiendo a los metadatos del programador

Los triggers de la Base de Datos

Haciendo backups con GBAK (1)

Haciendo backups con GBAK (2)

El índice del blog Firebird

El foro del blog Firebird21

 

Validando números de teléfono

2 comentarios

La operación más importante de todas en una Base de Datos es la introducción de datos válidos. Si los datos no son válidos entonces todo lo demás que hagamos (consultas, procesamientos) será incorrecto y no servirá, será inútil.

Un dato que a veces es importante validar y a veces no, es el número de teléfono.

Muchas veces el número de teléfono es simplemente informativo, está ahí pero prácticamente no se lo usa. Sin embargo en ocasiones es extremadamente importante que sea válido. Es para estos últimos casos que debemos asegurarnos de que pueda ser utilizado cuando se lo necesita.

¿Cómo validamos un número de teléfono?

Los números de teléfono completos siempre están compuestos de la siguiente forma:

  • Código del país o región
  • Código del área
  • Número local

Cada país o región grande tiene un número que lo identifica. Por ejemplo el código de Argentina es 54, el de Bolivia 591, el de Brasil 55, el de Estados Unidos 1, el de Paraguay 595, el de Puerto Rico 1787, etc. Para saber que se trata del código del país o región y no de un número local, se le antecede con el signo + o con doble cero. Es decir que escribir 00 ó escribir + es lo mismo, la misma cosa.

El código del área (el área puede ser una ciudad grande, un estado o provincia o departamento) siempre viene a continuación del código del país.

Y finalmente viene el número local.

Ejemplos:

+49 30 1234567 corresponde a Alemania (porque empieza con 49), Berlín (porque continúa con 30), y el 1234567 es el número de esa ciudad y país. También se lo podría haber escrito como. 00 49 30 1234567.

+54 351 1234567 corresponde a Argentina (porque empieza con 54), provincia de Córdoba (porque sigue con 351) y el 1234567 es el número de esa ciudad y de ese país al cual se desea llamar. También se lo podría escribir como: 00 54 351 1234567.

+595 21 123456 corresponde a Paraguay (porque empieza con 595), ciudad de Asunción (porque sigue con 21),  el 123456 es el número de esa ciudad y ese país al cual se desea llamar. También se lo podría escribir como: 00 595 21 123456.

¿Cómo diseñamos la tabla?

Es muy común que para guardar números de teléfono se defina una columna como CHAR o VARCHAR y luego una longitud suficientemente grande, como por ejemplo:

CREATE TABLE CLIENTES(
   CLI_IDENTI INTEGER,
   CLI_NOMBRE VARCHAR(80),
   CLI_TELEFO CHAR(20)
);

En este caso la columna CLI_TELEFO está definida como CHAR(20) y la mayoría de los diseñadores pueden creer que está bien. Sin embargo, no es lo correcto. ¿Por qué no es correcto? Porque los usuarios en la columna CLI_TELEFO podrían insertar datos así:

1234567

+595-21-1234567

021-1234567

21.1234567

00-595-21-1234567

Y si nuestra aplicación debe usar esos números para hacerles llamadas a los clientes será muy problemático conseguirlo. Lo correcto por lo tanto es:

CREATE TABLE CLIENTES(
   CLI_IDENTI INTEGER,         -- Identificador del Cliente
   CLI_NOMBRE VARCHAR(80),     -- Nombre del Cliente
   CLI_CODPAI CHAR(4),         -- Código del país al cual corresponde el teléfono
   CLI_CODARE CHAR(4),         -- Código del área dentro del país
   CLI_TELEFO CHAR(8)          -- Número de teléfono local
);

De esa manera nunca habrá confusión posible. Inclusive podríamos tener un trigger BEFORE INSERT OR UPDATE que verifique el código del país, algo como:

CREATE EXCEPTION 
   E_CODIGO_PAIS_INCORRECTO 'El código del país es incorrecto';
CREATE TRIGGER CLIENTES_BIU FOR CLIENTES
   ACTIVE BEFORE INSERT OR UPDATE
   POSITION 1
   AS
   BEGIN
 
      IF (NEW.CLI_CODPAI <> '54' AND NEW.CLI_CODPAI <> '595') THEN
         EXCEPTION E_CODIGO_PAIS_INCORRECTO;
 
   END;

Este trigger verifica que el país sea Argentina (código 54) o que sea Paraguay (código 595). Si los países fueran más de 2 entonces lo más conveniente sería tener una tabla, llamada por ejemplo PAISES en la cual se encontraran los códigos y los nombres de los países.

De todas maneras lo importante del trigger es que valida que el país ingresado sea solamente uno de los países válidos, evitando así que el usuario introduzca datos que no tienen sentido.

Análogamente a como se validó el código del país se podría validar el código del área. Algo como:

CREATE TRIGGER CLIENTES_BIU FOR CLIENTES
   ACTIVE BEFORE INSERT OR UPDATE
   POSITION 1
   AS
   BEGIN
 
      IF (NEW.CLI_CODPAI <> '54' AND NEW.CLI_CODPAI <> '595') THEN
         EXCEPTION E_CODIGO_PAIS_INCORRECTO;
 
      IF (NEW.CLI_CODPAI = '595') THEN BEGIN
         IF (NEW.CLI_CODARE <> '21' AND NEW.CLI_CODARE <> '61') THEN
            EXCEPTION E_CODIGO_AREA_INCORRECTO;
      END
 
   END;

Aquí, si el código del país es 595 el código del área debe ser 21 ó 61, ningún otro código de área será aceptado.

Conclusión:

Validar los números de teléfono a veces no es importante porque es un dato meramente informativo, pero hay ocasiones en que debemos asegurarnos de que sea un número válido. Para esos casos lo más conveniente es agruparlo según el código del país, del área, y número local. Esas tres columnas deben ser validadas para asegurarnos de que contienen números correctos.

Artículos relacionados:

Diseño de bases de datos. 1FN

El índice del blog Firebird21

El foro del blog Firebird21

Validando que el contenido de un CHAR o VARCHAR sea numérico

2 comentarios

Como sabes, en una columna de tipo CHAR o VARCHAR puedes escribir cualquier caracter que desees. Pero ¿y si quieres comprobar que solamente haya números allí?

Bien, hay varias técnicas para conseguir ese objetivo pero probablemente la más eficiente sea utilizar el predicado de comparación SIMILAR TO.

Caso 1. Comprobar que solamente hay números enteros

SELECT
   MiColumna1,
   MiColumna2,
   MiColumna3
FROM
   MiTabla
WHERE
   MiColumna1 SIMILAR TO '[[:DIGIT:]]*'

Aquí, solamente son permitidos los dígitos: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. Si en la columna MiColumna1 existe cualquier otro caracter entonces la condición del filtro (o sea, la condición escrita en el WHERE) será falsa.

Caso 2. Comprobar que solamente hay números decimales

SELECT
   MiColumna1,
   MiColumna2,
   MiColumna3
FROM
   MiTabla
WHERE
   MiColumna1 SIMILAR TO '[[:DIGIT:]]*.[[:DIGIT:]]*'

¿Qué le estamos diciendo aquí al Firebird? Que puede haber cualquier cantidad de dígitos, luego un punto que debe existir sí o sí, y luego cualquier cantidad de dígitos.

Caso 3. Comprobar que solamente haya números, pero estos pueden ser enteros o decimales

SELECT
   MiColumna1,
   MiColumna2,
   MiColumna3
FROM
   MiTabla
WHERE
   MiColumna1 SIMILAR TO '[[:DIGIT:]]*.?[[:DIGIT:]]*'

Este caso es muy parecido al anterior pero ahora el punto decimal no es requerido, o sea que puede existir o no existir. En consecuencia, se aceptarán los números enteros y también los números decimales.

Caso 4. Comprobando que todos los números sean negativos

SELECT
   MiColumna1,
   MiColumna2,
   MiColumna3
FROM
   MiTabla
WHERE
   MiColumna1 SIMILAR TO '\-[[:DIGIT:]]*.?[[:DIGIT:]]*' ESCAPE '\'

Para que la condición sea válida todos los números deben ser negativos.

Caso 5. Cuando algunos números pueden ser negativos

SELECT
   MiColumna1,
   MiColumna2,
   MiColumna3
FROM
   MiTabla
WHERE
   MiColumna1 SIMILAR TO '\-?[[:DIGIT:]]*.?[[:DIGIT:]]*' ESCAPE '\'

Este es un caso mucho más común que el anterior. Aquí, se aceptan tanto números positivos como números negativos

Caso 6. Cuando quieres comprobar lo contrario a los casos anteriores

¿Y qué haríamos cuando lo que queremos comprobar no es alguno de los casos anteriores sino su contrario? Por ejemplo queremos ver todas las filas que no tienen un contenido numérico. Pues es muy fácil, simplemente escribimos la palabra NOT antes del predicado SIMILAR TO.

SELECT
   MiColumna1,
   MiColumna2,
   MiColumna3
FROM
   MiTabla
WHERE
   MiColumna1 NOT SIMILAR TO '[[:DIGIT:]]*'

Aquí, veremos todas las columnas que no están compuestas exclusivamente por números enteros. O sea que esta consulta es la inversa a la del Caso 1. De forma parecida podríamos hacer para los otros casos, es decir escribiendo la palabra NOT delante de SIMILAR TO.

Conclusión:

El predicado de comparación SIMILAR TO es muy poderoso y nos permite comprobar que el contenido de una columna de tipo CHAR o VARCHAR sea lo que deseamos.

Artículos relacionados:

Los predicados de comparación
Usando SIMILAR TO
Validando un e-mail
El índice del blog Firebird21
El foro del blog Firebird21

Validando un e-mail

Deja un comentario

A veces necesitamos verificar que el e-mail que el usuario introdujo sea un e-mail válido, es decir que cumpla con las reglas para ser usado como tal.

¿Cómo lo validamos?

Bien, hay varias formas de hacerlo pero probablemente la más inteligente sea usando expresiones regulares.  Mediante una expresión regular comparamos un string cualquiera con un patrón de caracteres. El resultado de esa comparación puede ser verdadero o falso, desde luego.

Entonces, para validar un e-mail usando expresiones regulares podríamos escribir algo como:

SELECT
   IIF('minombre@midominio.com' SIMILAR TO '[[:ALNUM:]-_.]*@[[:ALNUM:]-_.]*', 'válido', 'no válido')
FROM
   RDB$DATABASE

El e-mail que queremos validar es minombre@midominio.com y para ello en el SELECT usamos SIMILAR TO. Al usar SIMILAR TO le estamos diciendo al Firebird que usaremos una expresión regular.

¿Y qué significa todo lo que está a continuación de SIMILAR TO?

:ALNUM: es la abreviación de alfanumérico, es decir que incluye todos los caracteres desde la ‘a’ hasta la ‘z’, desde la ‘A’ hasta la ‘Z’ y desde el ‘0’ hasta el ‘9’.

* significa que el caracter precedente puede encontrarse 0 ó muchas veces

@ significa que sí o sí debe existir el carácter @ en esa posición

[] significa que es una clase, o sea cualquiera de los miembros de esa clase pueden existir

Entonces, en castellano lo que dice esa expresión regular es: “Cualquier carácter alfanumérico, el guión, el guión bajo y el punto, pueden repetirse entre cero y muchas veces. El símbolo de arroba debe existir, es obligatorio. Y luego cualquier carácter alfanumérico, el guión, el guión bajo y el punto, pueden repetirse entre cero y muchas veces.”

Entonces, ahora nos falta verificar que realmente con esa expresión regular podemos validar e-mails, así que probamos con varios de ellos, como:

‘minombre@midominio.com’

‘MiNombre@MiDominio.com’

‘MiNombre123@MiDominio.com’

‘Mi_Nombre_123@Mi_Dominio.com’

‘Mi_Nombre_123@Mi_Dominio.com.py’

Y vemos que con todos ellos funciona perfectamente. Así que ahora probamos con e-mails que sabemos inválidos, como los siguientes:

‘minombremidominio’

‘minombremidominio.com’

‘.com’

Y vemos que efectivamente son rechazados.

Bien, la validación no es perfecta ya que acepta los siguientes e-mails:

‘@com’

‘@.com’

Pero de todas maneras es de una gran ayuda y simple de escribir. Hay varias formas de validar que no empiece con @, una de ellas es la siguiente:

SELECT
   IIF('@com' SIMILAR TO '[[:ALNUM:]-_.]*@[[:ALNUM:]-_.]*' and
       SUBSTRING('@com' FROM 1 FOR 1) <> '@', 'válida', 'no válida')
FROM
   RDB$DATABASE

Donde lo que hacemos es verificar que el primer carácter no sea una @. Desde luego que en general no estarás validando contra una constante de tipo string sino contra una columna. Por ejemplo para usarlo en un trigger podrías escribir algo como:

CREATE EXCEPTION E_EMAIL_NO_VALIDO 'El e-mail no es válido, verifícalo.';
CREATE TRIGGER BIU_CLIENTES FOR CLIENTES
   ACTIVE BEFORE INSERT OR UPDATE
   POSITION 1
AS
   DECLARE VARIABLE lcEmailOK CHAR(1);
BEGIN

   lcEmailOK = (SELECT
                   IIF(NEW.CLI_EMAILX
                   SIMILAR TO '[[:ALNUM:]-_.]*@[[:ALNUM:]-_.]*' AND
                   SUBSTRING(NEW.CLI_EMAILX FROM 1 FOR 1) <> '@', 'T', 'F')
                FROM
                   RDB$DATABASE);

   IF (lcEmailOK = 'F') THEN
      EXCEPTION E_EMAIL_NO_VALIDO;

END;

La gran mayoría de los usuarios si escriben mal un e-mail es porque se equivocaron al hacerlo, no por maldad sino por una equivocación humana. Para detectar algunas de esas equivocaciones podríamos mejorar un poco el trigger anterior, que ahora quedaría así:

CREATE TRIGGER BIU_CLIENTES FOR CLIENTES
   ACTIVE BEFORE INSERT OR UPDATE
   POSITION 0
AS
   DECLARE VARIABLE lcEmailOK CHAR(1);
BEGIN

   lcEmailOK = (SELECT
                   IIF(NEW.CLI_EMAILX
                   SIMILAR TO '[[:ALNUM:]-_.]*@[[:ALNUM:]-_.]*' AND
                   SUBSTRING(NEW.CLI_EMAILX FROM 1 FOR 1) <> '@', 'T', 'F')
                FROM
                   RDB$DATABASE);
   
   IF (lcEmailOK = 'T') THEN
      lcEmailOK = IIF(NEW.CLI_EMAILX CONTAINING '--', 'F', lcEmailOK);
   
   IF (lcEmailOK = 'T') THEN
      lcEmailOK = IIF(NEW.CLI_EMAILX CONTAINING '__', 'F', lcEmailOK);
   
   IF (lcEmailOK = 'T') THEN
      lcEmailOK = IIF(NEW.CLI_EMAILX CONTAINING '..', 'F', lcEmailOK);
   
   IF (lcEmailOK = 'F') THEN
      EXCEPTION E_EMAIL_NO_VALIDO;

END;

Donde lo que hacemos es verificar que no haya escrito dos guiones seguidos, dos guiones bajos seguidos, o dos puntos seguidos. Ninguna de esas posibilidades es normalmente usada en un e-mail, así que las rechazamos. Desde luego que podrías agregar otras comparaciones si te parecen necesarias.

Y de esta manera siempre que el usuario esté intentando insertar o actualizar los datos de un cliente se verificará que el e-mail de ese cliente sea un e-mail que cumple con las reglas mínimas para ser admitido como tal. Si no las cumple entonces se lanzará una excepción informándole del problema.

Artículos relacionados:

Los predicados de comparación

Usando SIMILAR TO

El índice del blog Firebird21

El foro del blog Firebird21

 

Backup y restore a una versión más nueva del Firebird

3 comentarios

A veces puedes necesitar que una Base de Datos creada con una versión anterior del Firebird sea usada con una versión más reciente.

Si no necesitas las características adicionales que introdujo la versión más reciente entonces te puedes conectar a esa Base de Datos sin problema.

Sin embargo, a veces necesitas de las características adicionales y en ese caso debes efectuar un ciclo backup/restore

Ejemplo:

MiBaseDatos.fdb fue creada con la versión 1.5 y ahora queremos usarla con la versión 2.5

Caso 1. No necesitamos de las nuevas características introducidas por la versión 2.5

Nos conectamos a la Base de Datos desde la versión 2.5 y podremos usarla sin problemas

Caso 2. Necesitamos algunas de las características introducidas por la versión 2.5 (que es el caso más común)

Aquí, debemos hacer un ciclo backup/restore para agregarle a la Base de Datos toda la funcionalidad de la versión 2.5

Primero, usando el Servidor del Firebird 1.5, en la ventana “Símbolo del sistema” del Windows escribimos:

SET ISC_USER=SYSDBA

SET ISC_PASSWORD=masterkey

GFIX -validate -full MiBaseDatos.fdb

GFIX -mend -full -ignore MiBaseDatos.fdb

GFIX -sweep MiBaseDatos.fdb

GBAK -backup -verbose -ignore -garbage MiBaseDatos.fdb MiBackup.fbk

Los dos SET (ISC_USER e ISC_PASSWORD) sirven para decirle al Windows que queremos usar esas variables y en consecuencia en los comandos GFIX y GBAK no necesitaremos estar introduciendo el usuario ni la contraseña. Es opcional, si prefieres escribir el usuario y la contraseña en cada comando GFIX y GBAK, entonces puedes obviar los dos SET

Si no usas un alias entonces debes escribir la ruta completa a tu Base de Datos, por ejemplo: D:\DATABASES\MiBaseDatos.fdb

Si usas un alias, entonces puedes escribir solamente el alias, por ejemplo: CONTA

El nombre del archivo de backup debe ser escrito con la ruta completa, por ejemplo: D:\BACKUPS\MiBackup.fbk

La opción -validate busca errores en la Base de Datos, los muestra y los repara

La opción -full se usa con la opción -validate para examinar todas las páginas y todos los registros y para liberar fragmentos de registro que no están asignados

La opción -mend marca a los registros corruptos para que al hacer el backup no sean copiados. De esta manera solamente los registros que están en buen estado son enviados al archivo de backup

La opción -ignore ignora los errores de checksum

La opción -sweep realiza el sweep inmediatamente

La opción -backup es opcional, sirve para aclarar (a nosotros, los humanos) que se quiere hacer un backup y no un restore

La opción -verbose muestra lo que está haciendo el programa GBAK

La opción -garbage no realiza la recolección de basura durante el backup y por lo tanto el backup terminará más rápido. Se la usa cuando tienes planeado hacer el restore o el sweep después del backup

 Ahora que nos aseguramos que nuestra Base de Datos está en buen estado y que hemos creado el archivo de backup pasamos al siguiente paso: restaurar el archivo de backup en una versión más reciente del Firebird

Para eso, usando la versión más reciente, escribimos:

GBAK -create -verbose -page_size 16384 MiBackp.fbk MiBaseDatos.fdb

La opción -create creará el archivo MiBaseDatos.fdb y éste no debe existir.

La opción -verbose muestra lo que está haciendo el programa GBAK

La opción -page_size cambia el tamaño de la página. Debes tener en cuenta que si aumentas el tamaño de la página (por ejemplo: la Base de Datos original tenía un tamaño de página de 4096 y se la restaura con un tamaño de página de 16384) aumentará también el tamaño de tu Base de Datos restaurada.

Al finalizar la restauración ya tendremos una nueva Base de Datos, completamente funcional y con todas las características de la versión más reciente del Firebird.

Artículo relacionado:

El índice del blog Firebird21

Older Entries