La forma más fácil de cambiar un dominio

1 comentario

Lo correcto es que cuando definimos la estructura de una tabla todas sus columnas referencien a un dominio. ¿Pero qué pasa si luego queremos cambiar el dominio de una columna?

Supongamos que en muchas tablas tenemos columnas que referencian al dominio D_NOMBRE25 (que es un VARCHAR(25)) y luego queremos usar D_NOMBRE30 (que es un VARCHAR(30)). En ese caso, ningún problema, porque pasamos de un número menor (25 en este ejemplo) a un número mayor (30 en este ejemplo).

O tenemos columnas que usan un dominio D_INTEGER que es un INTEGER y queremos pasarlas a D_BIGINT que es un BIGINT. Ningún problema tampoco, ya que pasamos de INTEGER a BIGINT, de menor a mayor, está todo ok.

Sin embargo encontraremos un problema cuando queremos hacer al revés: pasar de un número mayor a un número menor. Si queremos pasar de VARCHAR(30) a VARCHAR(25) el Firebird no lo permitirá. Tampoco permitirá pasar de BIGINT a INTEGER.

¿Por qué no permite?

Porque se puede perder precisión.

Pero yo estoy seguro que los datos cabrán bien.

No importa eso. Supongamos que quieres pasar de VARCHAR(30) a VARCHAR(25) y tú estás completamente seguro que todos los datos que tienes caben en un VARCHAR(25), que ninguno necesita más caracteres. Puedes tener razón, pero el Firebird no verifica eso. Él simplemente no permite pasar de una precisión mayor a una precisión menor. Aunque el mayor número que se guarde en una columna sea el 12, tampoco te permitirá cambiar de INTEGER a SMALLINT, por ejemplo.

¿Y cómo lo soluciono si necesito hacer esos cambios?

Tienes tres formas, dos largas y una corta.

Una forma larga es revisar todas las dependencias de cada columna, eliminar o poner comentarios en las columnas relacionadas, borrar la columna problemática y luego volver a insertarla, ya con el nuevo tipo de datos. Una vez que está insertada, recrear o quitar los comentarios de todas las dependencias.

Otra forma larga es agregar una columna adicional, con el nuevo tipo de datos, copiar todos los datos de la columna original a la nueva columna, cambiar todas las referencias a la columna original por referencias a la nueva columna y finalmente borrar la columna original.

En ambos casos, si no se trata solamente de una o dos columnas sino de decenas o de centenas de columnas, el trabajo será laborioso y demandará bastante tiempo.

Hay una forma más práctica: crear una nueva Base de Datos con los cambios deseados.

Ejemplo: Cambiar el dominio D_IDENTIFICADOR que es un BIGINT a INTEGER

DOMINIOS1

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

DOMINIOS2

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

DOMINIOS3

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

despues “Next”, “Next”, “Next”, “Finish” y “Close”.

Al finalizar el proceso, tendrás un script con el contenido de tu Base de Datos. Ahora ya es simplemente cuestión de buscar a D_IDENTIFICADOR y  cambiar su tipo de BIGINT a INTEGER.

DOMINIOS4

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

Una vez hecho eso, se ejecuta el script y listo, asunto solucionado.

DOMINIOS5

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

entonces, sin importar si el dominio D_IDENTIFICADOR había sido usado 40 veces, 70 veces, o lo que fuera, al crear una nueva Base de Datos su tipo de datos es el tipo de datos que queríamos que tuviera. Realizar todos estos pasos no tarda más de uno o dos minutos, una ganancia considerable de tiempo comparado con los otros dos métodos.

Advertencia:

Para asegurarte que todos los metadatos se hayan copiado, siempre es importante que hagas esa verificación.

DOMINIOS6

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

O sea, si la Base de Datos original tiene 44 dominios, la Base de Datos nueva también debe tener 44 dominios. Si la original tiene 80 tablas la nueva también debe tener 80 tablas, etc.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

Consultando sin importar mayúsculas ni acentos

1 comentario

A veces podríamos necesitar que la consulta nos traiga los datos sin importar que estén en mayúsculas, en minúsculas, acentuados o no acentuados. Por ejemplo queremos obtener: MARIA, MARÍA, maria, maría, Maria, María.

Para ello, debemos usar un ordenamiento (collation, en inglés) que termine con CI_AI ya que eso significa que al ordenar no diferencie entre mayúsculas y minúsculas ni entre vocales acentuadas y no acentuadas.

Al crear nuestra tabla, las columnas que deseamos que tengan esa característica deberían tener el atributo COLLATE ES_ES_CI_AI (si el character set de la Base de Datos es ISO8859_1) o UNICODE_CI_AI (si el character set es UTF8).

Ejemplo:

CREATE TABLE PERSONAS (
   PER_IDENTI INTEGER NOT NULL,
   PER_NOMBRE VARCHAR(40) COLLATE ES_ES_CI_AI,
   PER_APELLD VARCHAR(20) COLLATE ES_ES_CI_AI);

ALTER TABLE PERSONAS ADD CONSTRAINT PK_PERSONAS PRIMARY KEY (PER_IDENTI);

Si usamos el programa EMS SQL Manager para crear nuestras tablas entonces veríamos algo similar a esto:

INSENSITIVO3

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

Las columnas PER_NOMBRE y PER_APELLD de la tabla PERSONAS usan COLLATE ES_ES_CI_AI y por lo tanto al escribir esta consulta:

SELECT
   PER_NOMBRE,
   PER_APELLD
FROM
   PERSONAS
WHERE
   PER_NOMBRE LIKE 'MARIA%'

esto será lo que obtendremos:

INSENSITIVO1

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

o sea, todas las “María” que tenemos en nuestra tabla de PERSONAS, sin importar como se escribieron los nombres de esas personas. Claro que en el SELECT podríamos escribir UPPER(PER_NOMBRE) y UPPER(PER_APELLD) para que todos los nombres y apellidos estén en mayúsculas, y en ese caso obtendríamos:

INSENSITIVO2

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

lo cual, en general por una cuestión de estética, sería preferible. Fíjate que los nombres que se escribieron sin acentos siguen estando sin acentos, pero son mostrados, que es lo que queríamos conseguir. Si en nuestra tabla no hubiéramos usado COLLATE ES_ES_CI_AI entonces el SELECT no nos hubiera mostrado los nombres que tienen acento.

Artículo relacionado:

El índice del blog Firebird21

Forced writes

1 comentario

En cada una de nuestras bases de datos podemos determinar si los cambios que se realizan en ella se escribirán inmediatamente en el disco duro (FORCED WRITES = ON) o si se escribirán después de un tiempo (FORCED WRITES = OFF)

Si Forced Writes está en ON entonces tenemos dos ventajas:

  1. Los cambios se guardan en forma permanente. Un corte de la energía eléctrica no afectará a los datos ya guardados
  2. Los cambios se guardan siempre en el orden correcto. Por ejemplo, primero se guarda el registro y luego se guarda la entrada del índice que corresponde a ese registro

Si Forced Writes está en OFF tenemos una ventaja:

  1. Los datos se guardan mucho más rápidamente

Entonces ¿cuál es más conveniente?

En general, tener a Forced Writes en ON es lo mejor, esto es aún más notorio en Windows que en Linux. Porque cuando está en ON nos aseguramos que nunca haya pérdida de datos y que la Base de Datos siempre tenga consistencia. Si está en OFF, un corte de la energía eléctrica casi con seguridad hará que la Base de Datos se corrompa. Esa corrupción podría solucionarse, hay programas especializados en esa tarea, pero de todas maneras provocará molestias, inconvenientes y pérdida de tiempo y de dinero.

Podríamos tener Forced Writes en OFF solamente en casos especiales y muy puntuales, por ejemplo para insertar millones de filas desde una tabla externa. Una vez que esa inserción masiva ha finalizado volvemos a colocar Forced Writes en ON.

¿Cómo podemos saber si nuestra Base de Datos tiene Forced Writes en ON o en OFF?

Ejecutando el programa GSTAT.EXE que se encuentra en la carpeta \BIN de donde instalaste el Servidor del Firebird. A ese programa le pones como parámetros el nombre completo o el alias de la Base de Datos que deseas verificar y en la entrada Attributes te mostrará el estado actual de Forced Writes.

FORCED1

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

Después de escribir GSTAT y el alias de una Base de Datos se puede ver el estado de Forced Writes, que en este caso está en ON. O sea, que es lo correcto durante el uso normal.

¿Cómo se puede cambiar el estado de Forced Writes?

Mediante el programa GFIX.EXE, el cual tiene una opción llamada -write que puede ser sync (para que Forced Writes esté en ON) o async (para que Forced Writes esté en OFF)

FORCED2

 

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

Como puedes ver, al usar la opción -writes async del programa GFIX.EXE (flecha 1) el estado de Forced Writes se cambió a OFF como nos muestra el programa GSTAT.EXE (flecha 2) porque a continuación de Attributes no hay algo escrito.

Conclusión:

En general, lo conveniente es que Forced Writes esté en ON porque eso nos asegura que todos los datos se guarden y que se guarden en el orden correcto. Un corte de la energía eléctrica no ocasionará pérdidas de datos. Poner a Forced Writes en OFF puede ser muy útil cuando debemos realizar una entrada masiva de datos, pero no debemos olvidarnos de volver a ponerlo en ON apenas esa entrada masiva de datos finalice.

Artículo relacionado:

El índice del blog Firebird21

 

 

 

Usando EMS SQL Manager para copiar entre bases de datos

6 comentarios

A veces tenemos dos bases de datos que tienen tablas iguales o similares. Y como en una/s tabla/s de una de esas bases de datos ya se han insertado bastantes filas, nos gustaría simplemente copiar esas filas a la otra Base de Datos.

Hay muchas formas de realizar esa tarea, esta es una de ellas, es muy útil cuando son varias las tablas cuyos contenidos deseamos copiar aunque por supuesto podríamos usarla para copiar los datos de una sola tabla.

Veamos un ejemplo:

En una Base de Datos tenemos una tabla llamada BANCOS, que ya tiene insertadas varias filas:

EMS1

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

y esas mismas filas queremos copiar en la tabla BANCOS de otra Base de Datos. Para ello, hacemos clic con el botón derecho sobre el nombre de la Base de Datos original, o sea la que tiene las filas que deseamos insertar.

EMS2

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

en el menú contextual que nos aparece elegimos Tasks y luego Extract Database…

EMS3

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

a continuación marcamos la opción Extract all metadata and data of the database y hacemos clic en el botón “Next”

EMS4

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

ahora, elegimos la opción Automatically load to Script Editor y luego clic sobre el botón “Next”

EMS5

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

en la siguiente pantalla elegimos como queremos extraer las columnas de tipo BLOB, y en la que aparece a continuación podemos desmarcar todas las opciones porque no nos harán falta para lo que deseamos hacer

EMS6

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

después de un clic sobre el botón “Next” veremos esta pantalla:

EMS7

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

donde tendremos que hacer clic sobre el botón “Finish” para que empiece el proceso (sí, ya sé que es medio raro tener que hacer clic sobre el botón “Finish” para que algo empiece, pero bueno, así está). Cuando el proceso se haya completado hay que hacer clic sobre el botón “Close” y nos mostrará todos los metadatos y todos los datos de nuestra Base de Datos en forma de script.

Así que ahora buscamos el comando INSERT INTO BANCOS y seleccionamos y copiamos (con CTRL+C) todas las filas que  tienen ese comando:

EMS8

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

después abrimos la otra Base de Datos, y elegimos las opciones Tools y a continuación New SQL Script

EMS9

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

en la ventanita que aparece pegamos con CTRL+V lo los comandos INSERT INTO que habíamos copiado y finalmente hacemos clic en la opción Execute script

EMS10

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

y listo, asunto concluido. Todas las filas de la tabla BANCOS que se encontraban en una Base de Datos fueron copiadas a la otra Base de Datos, muy fácilmente y con muy poco esfuerzo.

Hay que aclarar que esta no es la forma más rápida de copiar las filas, pero es una forma práctica y sencilla cuando las filas a copiar no son muchas, quizás hasta unas pocas centenas y además si es algo esporádico. Si las filas son muchas o si este proceso de copia se va a realizar frecuentemente entonces hay que elegir otro método.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

Aplicaciones multimoneda

7 comentarios

Muchas veces nos gustaría que nuestra aplicación fuera multi-moneda, o sea que los importes puedan ingresarse en nuestra moneda local, en dólares, en euros, en libras esterlinas, en rublos, en yenes, etc., en la moneda que el usuario quiera.

Eso podemos conseguirlo fácilmente de la siguiente manera:

  1. Teniendo una tabla llamada MONEDAS
  2. En cada tabla donde se guardan importes guardar: cual fue la moneda utilizada y cual fue la cotización de ella. La cotización de la moneda usada por defecto (generalmente será la moneda local) siempre será 1.

MULTIMONEDA1

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

MULTIMONEDA3

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

MULTIMONEDA2

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

MULTIMONEDA4

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

Así que si ahora escribimos un SELECT como este:

SELECT
   MVC_TOTALX * MVC_COTIZA AS TOTAL_MONEDA_POR_DEFECTO
FROM
   MOVIMCAB

obtendremos todos los importes en nuestra moneda por defecto (la cual generalmente será nuestra moneda local):

MULTIMONEDA5

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

Conclusión:

Siempre es conveniente que todas nuestras aplicaciones sean multimoneda, de esa manera podremos venderlas a mayor cantidad de clientes potenciales, muchas empresas u organizaciones necesitan que sus informes sean mostrados en más de una moneda.

Artículo relacionado:

El índice del blog Firebird21

Tablas sin una PRIMARY KEY

5 comentarios

Sabemos muy bien que todas las tablas deben tener sí o sí una Primary Key (la única excepción son las tablas que siempre tendrán una fila y nunca tendrán más de una fila) pero ¿estamos seguros de que todas nuestras tablas tienen una Primary Key?

Si una tabla (que tiene o puede tener más de una fila) no tiene una Primary Key eso es un error y debemos corregirlo urgentemente. Desde luego que puedes revisar las tablas una por una, manualmente, pero eso no sería profesional.

Aquí hay un stored procedure para averiguarlo (por supuesto que no es la única forma, pero quiero mostrar como hacerlo así):

SET TERM ^ ;

CREATE PROCEDURE TABLAS_SIN_PRIMARY_KEY
   RETURNS(
      TCNOMBRETABLA VARCHAR(32))
AS
   DECLARE VARIABLE lcNombreTabla VARCHAR(32);
   DECLARE VARIABLE lcRestriccion VARCHAR(20);
   DECLARE VARIABLE lcCC VARCHAR(32);
   DECLARE VARIABLE lcMostrar VARCHAR(1);
BEGIN

   lcCC = '***';

   lcMostrar = 'F';

   FOR SELECT
      RDB$RELATION_NAME,
      RDB$CONSTRAINT_TYPE
   FROM
      RDB$RELATION_CONSTRAINTS
   ORDER BY
      RDB$RELATION_NAME
   INTO
      :lcNombreTabla,
      :lcRestriccion
   DO BEGIN
      IF (lcNombreTabla <> lcCC) THEN BEGIN
         IF (lcMostrar = 'T') THEN BEGIN
            tcNombreTabla = lcCC;
            SUSPEND;
         END
         lcCC = lcNombreTabla;
         lcMostrar = 'T';
      END
      IF (lcRestriccion = 'PRIMARY KEY') THEN
         lcMostrar = 'F';
   END

END^

SET TERM ; ^

Este stored procedure es seleccionable y por lo tanto lo debes ejecutar así:


SELECT * FROM TABLAS_SIN_PRIMARY_KEY

Artículos relacionados:

Entendiendo a las Primary Keys

El índice del blog Firebird21

Capturando excepciones del usuario

10 comentarios

Así como podemos capturar las excepciones del Firebird, las cuales tienen un número en SQLCODE o un código en GDSCODE también podemos capturar nuestras propias excepciones.

Ejemplo:

Tenemos una tabla llamada PRODUCTOS en la cual (además de otros datos) guardamos el precio de costo y el precio de venta de cada producto. Es política de la empresa que nunca el precio de venta pueda ser menor que el precio de costo aumentado en un 5%. En otras palabras, que el porcentaje de ganancia siempre debe ser 5% o más.

Esta es nuestra tabla PRODUCTOS:

CAPTURA1

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

Y esta es la tabla ERRORES, donde registramos todos los errores que vamos encontrando:

CAPTURA2

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

Esta es una excepción, que será lanzada cuando el precio de venta sea menor que el precio de costo aumentado en un 5%

CREATE EXCEPTION E_PORCENTAJE_MENOR_5 'El porcentaje de ganancia es menos que el 5%.';

Y este es un trigger de la tabla PRODUCTOS donde validamos que los precios de venta siempre sean al menos un 5% superiores al precio de costo. Y cuando no ocurre eso, insertamos una fila en la tabla ERRORES.

CREATE TRIGGER PRODUCTOS_BIU FOR PRODUCTOS
       ACTIVE BEFORE INSERT OR UPDATE
       POSITION 1
AS
BEGIN

   IF (NEW.PRD_PREVTA * 100 / NEW.PRD_PRECTO - 100 < 5) THEN
      EXCEPTION E_PORCENTAJE_MENOR_5
      'El porcentaje de ganancia es menor que el 5%. Producto = ' || NEW.PRD_NOMBRE ;

   WHEN EXCEPTION E_PORCENTAJE_MENOR_5 DO
      IN AUTONOMOUS TRANSACTION DO
         INSERT INTO ERRORES
               (ERR_MODULO, ERR_COMENT)
        VALUES ('PRODUCTOS_BIU' , 'El % de ganancia del producto *' || TRIM(NEW.PRD_NOMBRE) || '* es menor que 5%') ;

END

Fíjate que se sobreescribe el mensaje de la excepción y usamos un nuevo mensaje, personalizado, para indicar el nombre del producto que tuvo el problema. Desde luego que no es obligatorio sobreescribir el mensaje de la excepción, podríamos haber usado el mensaje por defecto, pero al sobreescribirlo tenemos más información. También cuando atrapamos la excepción indicamos el nombre del producto problemático para que al revisar la tabla ERRORES sea fácil saber cual fue ese producto.

Si ahora escribimos:

INSERT INTO PRODUCTOS (PRD_IDENTI, PRD_NOMBRE, PRD_PRECTO, PRD_PREVTA)
VALUES (0, 'COCA COLA 1 LITRO', 45, 60)

estará todo bien, ningún problema porque el porcentaje de ganancia es mayor que el 5% (es del 33%)

Sin embargo, si escribimos:

INSERT INTO PRODUCTOS (PRD_IDENTI, PRD_NOMBRE, PRD_PRECTO, PRD_PREVTA)
VALUES (0, 'FANTA NARANJA 1 LITRO', 45, 46)

no se insertará el producto ‘FANTA NARANJA 1 LITRO’ en la tabla PRODUCTOS. ¿Por qué no? porque ocurrió una excepción, (el porcentaje de ganancia es menor que el 5%) y cuando ocurre una excepción todo lo que se había hecho hasta ese momento se desecha. Por lo tanto el INSERT a la tabla PRODUCTOS es desechado. Pero el INSERT a la tabla ERRORES sí funciona, porque ese INSERT estaba dentro de una IN AUTONOMOUS TRANSACTION y por lo tanto se guarda sí o sí.

Si ahora revisamos el contenido de la tabla ERRORES veremos esto:

CAPTURA3

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

Y hemos conseguido lo que buscábamos: que cuando ocurra una excepción del usuario la capturemos.

Conclusión:

Así como podemos capturar las excepciones del Firebird (las que usan SQLCODE y GDSCODE) también podemos capturar nuestras propias excepciones. Siempre es conveniente registrar en una tabla las excepciones ocurridas para más adelante poder revisar esa tabla y descubrir que fue lo que se hizo mal.

Artículos relacionados:

Capturando excepciones

Capturando excepciones. Códigos de error predeterminados

¿Cuáles errores no se pueden atrapar?

El índice del blog Firebird21

El foro del blog Firebird21

 

¿Cuáles errores no se pueden atrapar?

3 comentarios

No todos los errores podemos atrapar con las excepciones. Eso sería lo ideal pero el estándar SQL supone que algunos errores serán evitados a nivel de lenguaje de programación, no a nivel de la Base de Datos.

Veamos un ejemplo:

Tenemos una tabla llamada NOMBRES, con esta estructura:

ERRORES1

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

y una tabla para registrar los ERRORES que tiene esta estructura:

ERRORES2

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

También un stored procedure para actualizar o insertar filas a la tabla NOMBRES, cuyo código fuente es el siguiente:

SET TERM ^ ;

CREATE PROCEDURE GRABAR_NOMBRES(
   IDENTI TYPE OF COLUMN NOMBRES.NOM_IDENTI,
   NOMBRE TYPE OF COLUMN NOMBRES.NOM_NOMBRE)
AS
BEGIN

   UPDATE OR INSERT INTO NOMBRES
                   (NOM_IDENTI, NOM_NOMBRE)
            VALUES (:Identi   , :Nombre ) ;

   WHEN ANY DO
      IN AUTONOMOUS TRANSACTION DO
         INSERT INTO ERRORES
                    (ERR_MODULO      , ERR_COMENT)
             VALUES ('GRABAR_NOMBRES', 'No se grabó el nombre');

END^

SET TERM ; ^

La idea sería, que si hay un error en el UPDATE OR INSERT se agregue una fila a la tabla ERRORES para tener registrado que ocurrió ese error. Sin embargo, si escribimos algo como:


EXECUTE PROCEDURE GRABAR_NOMBRES(0, 'JUAN567890123456789012345');

no se inserta una fila en la tabla ERRORES. Tampoco se insertará si en lugar de un stored procedure escribimos un trigger:

SET TERM ^ ;

CREATE TRIGGER NOMBRES_BIU FOR NOMBRES
   ACTIVE BEFORE INSERT OR UPDATE
   POSITION 1
AS
BEGIN

   IN AUTONOMOUS TRANSACTION DO
      INSERT INTO ERRORES
                 (ERR_MODULO   , ERR_COMENT)
          VALUES ('NOMBRES_BIU', 'Ocurrió un error');

END^

SET TERM ; ^

¿Por qué, qué pasó, por qué en ninguno de esos casos conseguimos que se inserte una fila en la tabla ERRORES?

La respuesta es que el Firebird (y todos los SGBD que cumplen con el estándar SQL) cuando encuentra que hay un error en los tipos de datos de un INSERT o de un UPDATE ya rechaza esa inserción o actualización, no continúa.

En nuestro ejemplo el dato que queríamos insertar en la columna NOM_NOMBRE tenía más de 20 caracteres y la columna NOM_NOMBRE está definida como VARCHAR(20). Lo mismo hubiera sucedido si por ejemplo en una columna numérica queremos guardar un dato alfabético: el Firebird lo rechaza sin contemplaciones.

Y eso implica que no podremos agregarle una fila a la tabla ERRORES que nos diga algo como: “la cantidad de caracteres que se quisieron grabar en la columna NOM_NOMBRE es mayor que 20”.

Por lo tanto, la única alternativa posible es que en nuestro programa, en nuestro lenguaje de programación, realicemos esa validación.

Conclusión:

Hay algunos errores que no se pueden atrapar con la construcción WHEN …. DO, y esos son los errores debidos a tipos de datos incorrectos. Por ejemplo, si en una columna definida como VARCHAR(20) queremos guardar más de 20 caracteres nuestro intento será rechazado pero no se ejecutarán las instrucciones del WHEN … DO que tengamos a continuación. Lo mismo ocurriría si en una columna de tipo numérico queremos insertar caracteres alfabéticos. O de tipo fecha. O de tipo hora, etc.

Artículo relacionado:

El índice del blog Firebird21

OT: Coursera

3 comentarios

Hace unos años varias Universidades de primer nivel mundial lideradas por la Stanford University decidieron enseñar cursos gratis para ayudar a que los conocimientos universitarios se popularicen y que no fuera un requisito tener dinero para poder estudiar. Ese proyecto se llama Coursera y allí puede encontrar cursos relacionados con muchas disciplinas: Informática, Arte, Medicina, Ingeniería, Matemática, etc.

Los cursos se imparten en varios idiomas, castellano incluido, aunque la gran mayoría están en inglés. Todos los profesores de estos cursos son también profesores en las Universidades que apoyan el proyecto. Todos los cursos son totalmente gratis pero si quieres un certificado cuando finalices un curso debes abonar una pequeña suma, mayormente para gastos de envío.

Algunas de las Universidades participantes son:

  • Stanford University
  • Princeton University
  • Tecnológico de Monterrey (México)
  • Northwestern University
  • University of Washington
  • University of Pennsylvania
  • The Ohio State University
  • University of Maryland
  • Peking University (China)
  • The University of Chicago

y hay un montón más.

Este es el enlace:

https://www.coursera.org/

y aquí hay un curso que quizás te interese:

https://class.coursera.org/db/class

se trata sobre diseño de Bases de Datos. Está en inglés, pero es un inglés muy fácil de entender y supongo que en esta época la gran mayoría de los lectores de este blog podrá obtener provecho de ese curso.

El único requisito es tener una computadora con conexión a Internet y por supuesto algunos conocimientos básicos relacionados con el curso que vas a tomar. Éstos generalmente duran unas pocas semanas. Como hay una gran variedad de cursos, puedes comentarle sobre Coursera a tus amigos, conocidos y familiares, quizás a algunos de ellos les interese también aprender cosas nuevas a nivel universitario.

Artículo relacionado:

El índice del blog Firebird21

Insertando datos aleatorios

15 comentarios

Muchas veces, generalmente cuando estamos haciendo pruebas para verificar el correcto funcionamiento de nuestros programas, necesitamos que nuestras tablas tengan muchos datos, así podemos comprobar que todo está funcionando bien … o no.

Desde luego que una posibilidad es ingresar esos datos manualmente, con un montón de INSERTs y luego un COMMIT. La situación se complica cuando los datos que necesitamos insertar no son unos pocos, sino cientos o miles. Para esos casos lo mejor es automatizar el proceso y que sea un stored procedure el encargado de realizar la tarea.

Para mostrar el concepto creé una tabla llamada NOMBRES, que tiene esta estructura:

ALEATORIO2

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

y le agregué (manualmente) estas filas:

ALEATORIO3

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

Después creé una tabla llamada APELLIDOS con esta estructura:

ALEATORIO4

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

Y le agregué, también manualmente, estas filas:

ALEATORIO5

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

A continuación creé otra tabla, llamada ALEATORIOS, con esta estructura:

ALEATORIO1

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

Y para insertar automáticamente filas en la tabla ALEATORIOS escribí este stored procedure:

SET TERM ^ ;

CREATE PROCEDURE INSERTAR_DATOS_ALEATORIOS(
   TNCANTIDADFILAS INTEGER)
AS
   DECLARE VARIABLE lnI INTEGER;
   DECLARE VARIABLE lnJ SMALLINT;
   DECLARE VARIABLE lnDia SMALLINT;
   DECLARE VARIABLE lnMes SMALLINT;
   DECLARE VARIABLE lnDiasMes SMALLINT;
   DECLARE VARIABLE ldFechax DATE;
   DECLARE VARIABLE lnHoras SMALLINT;
   DECLARE VARIABLE lnMinutos SMALLINT;
   DECLARE VARIABLE ltHoraxx TIME;
   DECLARE VARIABLE lnNumero SMALLINT;
   DECLARE VARIABLE lnFilasTabla INTEGER;
   DECLARE VARIABLE lnFilaElegida INTEGER;
   DECLARE VARIABLE lnCantidadNombres SMALLINT;
   DECLARE VARIABLE lcNombre VARCHAR(40);
   DECLARE VARIABLE lnCantidadApellidos SMALLINT;
   DECLARE VARIABLE lcApelld VARCHAR(40);
BEGIN

   lnI = 1;

   WHILE (lnI <= tnCantidadFilas) DO BEGIN
      -- Fechas aleatorias
      lnMes     = 1 + FLOOR(RAND() * 12);
      lnDiasMes = DECODE(lnMes, 1, 31, 2, 28, 3, 31, 4, 30, 5, 31, 6, 30, 7, 31, 8, 31, 9, 30, 10, 31, 11, 30, 12, 31);
      lnDia     = 1 + FLOOR(RAND() * lnDiasMes) ;
      ldFechax  = CAST(lnDia || '.' || lnMes || '.' || 2013 AS DATE);
      -- Horas aleatorias
      lnHoras   = FLOOR(RAND() * 24);
      lnMinutos = FLOOR(RAND() * 60);
      ltHoraxx  = CAST(lnHoras || ':' || lnMinutos AS TIME);
      -- Números aleatorios
      lnNumero = 1 + FLOOR(RAND() * 32767);
      -- Nombres aleatorios. Pueden haber 1, 2, ó 3 nombres
      lnCantidadNombres = 1 + FLOOR(RAND() * 3);
      lnFilasTabla      = (SELECT COUNT(*) FROM NOMBRES);
      lcNombre          = '';
      lnJ               = 1;
      WHILE (lnJ <= lnCantidadNombres) DO BEGIN
         lnFilaElegida = 1 + FLOOR(RAND() * lnFilasTabla);
         lcNombre      = lcNombre || (SELECT TRIM(NOM_NOMBRE) FROM NOMBRES WHERE NOM_IDENTI = :lnFilaElegida) ||' ';
         lnJ           = lnJ + 1;
      END
      -- Apellidos aleatorios. Pueden haber 1 ó 2 apellidos
      lnCantidadApellidos = 1 + FLOOR(RAND() * 2);
      lnFilasTabla        = (SELECT COUNT(*) FROM APELLIDOS);
      lcApelld            = '';
      lnJ                 = 1;
      WHILE (lnJ <= lnCantidadApellidos) DO BEGIN
         lnFilaElegida = 1 + FLOOR(RAND() * lnFilasTabla);
         lcApelld      = lcApelld || (SELECT TRIM(APE_APELLD) FROM APELLIDOS WHERE APE_IDENTI = :lnFilaElegida) || ' ';
         lnJ           = lnJ + 1;
      END
      -- Se insertan en la tabla los datos obtenidos
      INSERT INTO ALEATORIOS (ALE_IDENTI, ALE_FECHAX, ALE_HORAXX, ALE_NUMERO, ALE_NOMBRE, ALE_APELLD)
                      VALUES ( 0, :ldFechax , :ltHoraxx , :lnNumero , :lcNombre , :lcApelld ) ;
      lnI = lnI + 1;
   END

END^

SET TERM ; ^

Este stored procedure recibe como parámetro la cantidad de filas que se desean insertar, genera aleatoriamente los datos de las columnas y luego los inserta en la tabla ALEATORIOS. Entonces, nos será muy fácil insertar 1.000 filas, 25.000 filas, 3.000.000 de filas, o las que necesitemos. Desde luego que en tu caso la tabla no se llamará ALEATORIOS, tendrá otro nombre; así mismo en tu caso los nombres de las columnas seguramente serán distintos. Este es un ejemplo, para que captes la idea y te resulte muy fácil hacer algo similar cuando lo necesites.

Para verificar que funciona bien, escribí:

EXECUTE PROCEDURE INSERTAR_DATOS_ALEATORIOS(25);

y luego verifiqué el contenido de la tabla ALEATORIOS y esto fue lo que obtuve:

ALEATORIO6

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

O sea, 25 filas con contenido totalmente al azar. Por supuesto que si tú ejecutas el stored procedure obtendrás datos distintos, pues justamente de eso se trata: de tener datos distintos, al azar, como sucede en la vida real, donde generalmente no podemos saber lo que ocurrirá.

Supongo que te habrás fijado que algunas personas tienen un solo nombre, algunas tienen dos nombres y algunas tienen tres nombres. Así mismo algunas personas tienen un solo apellido y otras personas tienen dos apellidos.

Conclusión:

Poder insertar rápidamente y fácilmente miles o millones de filas a nuestras tablas nos será de gran ayuda, sobre todo cuando estamos haciendo pruebas para verificar el correcto funcionamiento de nuestros programas.

La cantidad de nombres que tendrá una persona se encuentra en la variable lnCantidadNombres y la cantidad de apellidos en la variable lnCantidadApellidos. Hice de esta manera para que se vea más real, pero no siempre necesitarás tener más de un nombre o más de un apellido.

Inclusive se podría haberle puesto pesos a la cantidad de nombres y de apellidos. Por ejemplo que el 80% de las personas tengan 2 nombres, el 15% que tenga 1 nombre y el restante 5% que tenga 3 nombres, cosas así.

Si quieres números que vayan por ejemplo desde el 32 hasta el 212, y solamente números en ese rango, tendrías que escribir:

lnNumero = 32 + FLOOR(RAND() * 180)

donde 32 es el número más bajo que se podría obtener y 180 es la cantidad de números distintos que se podrían obtener.

El número más bajo que se puede obtener con FLOOR(RAND() * n) es el 0, por eso si se quiere que los números aleatorios empiecen desde otro número hay que hacer una suma.

Artículo relacionado:

El índice del blog Firebird21

Older Entries