Error al ejecutar GSEC

5 comentarios

Como seguramente sabes, el programa GSEC.EXE se utiliza para agregar usuarios, cambiar datos de los usuarios, eliminar usuarios y ver cuales son los usuarios admitidos.

Pero si tienes más de una instancia del Firebird instalada entonces a veces podrías ver una pantalla de error similar a la siguiente:

GSEC1

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

¿Qué significa ese error número 335545106?

Que no puede abrir la Base de Datos en la cual se guardan los nombres y las contraseñas de los usuarios.

En Firebird 2.x el nombre de esa Base de Datos es SECURITY2.FDB y la encontrarás en la misma carpeta donde instalaste al Firebird.

¿Cómo se soluciona ese error?

Si tienes más de una instancia del Firebird instalada entonces cada instancia usa su propio puerto (o así debería ser si se instaló correctamente). Por lo tanto la solución es indicarle que abra el archivo SECURITY2.FDB que corresponde a la instancia que estemos usando en ese momento.

GSEC2

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

Como puedes ver en la Captura 2. se indico la IP de la computadora y el puerto 3050. Como ese es el puerto que le corresponde a la instancia del Firebird que nos interesa, funcionó perfectamente.

Después de la opción -database se escribió la ruta completa entre comillas porque hay un espacio en blanco en el medio, y en esos casos el Windows exige que se usen comillas.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

Anuncios

¿Qué datos guardar de una Factura?

16 comentarios

Este tema ya ha sido discutido en artículos anteriores pero siempre viene bien darle un breve repaso. Veo que aún hay mucha gente que tiene problemas conceptuales con el mismo.

El punto es muy sencillo: cualquier documento que pueda ser impreso y cuyos datos estaban correctos en el momento de la impresión debe poder ser vuelto a imprimir exactamente igual a la primera vez.

¿Por qué eso?

Porque en el momento de la impresión de un documento esos datos que se ven en él son los correctos (o al menos se supone que los son). Si más tarde por cualquier motivo se necesita reimprimir ese mismo documento entonces la reimpresión debe ser una copia exacta de lo que se imprimió la primera vez. Si eso no es así podrás tener problemas con la autoridad impositiva de tu país, porque a la gente del gobierno no le gusta que los documentos sean distintos.

Y tienen toda la razón del mundo, si a un cliente le diste una Factura por un total de 1.000 dólares y unos meses después al consultar esa Factura se ve que la venta fue de sólo 800 dólares hay un grave problema allí. Y en la mayoría de los países alguien se irá a la cárcel por eso o al menos pagará una fuerte multa, y todo eso podría haberse evitado si la Base de Datos hubiera estado cuidadosamente diseñada.

Entonces, lo que se imprime y sale de la Empresa no se debe normalizar.

En la gran mayoría de los casos, lo mejor y lo recomendable es que las tablas estén normalizadas. En este caso especial lo correcto es que no lo estén.

¿Por qué?

Porque si en la Factura el cliente tiene una dirección y un teléfono, después se muda, se actualizan su dirección y su teléfono en la tabla de CLIENTES, se reimprime la Factura y aparecerán en ella los nuevos datos, y eso está mal. Deberían aparecer los datos originales.

Eso hasta podría ser conversable con la gente del gobierno porque no hubo evasión impositiva ahí, pero ¿y si se cambió la cantidad de productos vendidos o los precios de venta?

Eso ya es otra cosa y un problema gravísimo. Porque eso sí puede provocar evasión impositiva, un delito muy grave en casi todos los países.

Entonces, en nuestra tabla de detalles de ventas tendríamos que guardar el Identificador del Producto vendido y también su código y también su nombre.

¿Y cómo evitamos que se modifiquen la cantidad vendida o el precio de venta o cualquier otro dato de detalle?

Con un trigger que se dispara cuando se quiere actualizar una fila, y envía una excepción.

IF UPDATING THEN
   EXCEPTION E_NO_SE_PUEDE_MODIFICAR_ESTA_FILA

¿Y cómo evitamos que se borre una fila?

Con un trigger que se dispara cuando se quiere borrar una fila y envía una excepción.

IF DELETING THEN
   EXCEPTION E_NO_SE_PUEDE_BORRAR_ESTA_FILA

¿Y si está todo mal y queremos borrar esa Factura?

Para este caso legítimo en nuestra tabla de cabecera tendremos una columna que nos indique que la Factura está anulada. Eso implica que la Factura jamás se borra de la tabla, simplemente se le pone una marca de que sus datos no deben ser utilizados en la mayoría de los informes.

Conclusión:

Los datos que se imprimen en documentos que salen de la Empresa deben ser siempre los mismos, no importa cuando ni cuantas veces se reimprima ese documento. Si eso no se hace así entonces alguna vez se podrán tener graves problemas con los clientes, con los proveedores o mucho peor, con el gobierno.

Para asegurar de que los datos sean siempre los mismos, las tablas donde se encuentran los datos que se imprimirán no deben estar normalizadas. En general, en la gran mayoría de los casos lo correcto es que las tablas sí estén normalizadas, pero en este caso particular (documentos que salen de la Empresa) lo correcto y lo recomendable es que no lo estén.

Para evitar que una fila se modifique podemos usar un trigger que impedirá las modificaciones. Para evitar que una fila sea borrada podemos usar un trigger que impedirá el borrado. Por supuesto que podremos usar un solo trigger para realizar ambas tareas.

Haciendo así nos aseguraremos de que siempre, sin importar cuanto tiempo haya pasado, nuestra Base de Datos mostrará información consistente.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

 

Borrando filas cuando la condición depende de otra tabla

1 comentario

A veces podemos encontrarnos con situaciones donde necesitamos borrar algunas filas pero la condición de borrado depende de otra tabla.

Por ejemplo en una institución educativa tenemos dos tablas: ALUMNOS e INSCRIPCIONES. Evidentemente el alumno no puede inscribirse antes de nacer. Si lo tenemos inscripto antes de su fecha de nacimiento eso es un error y debemos borrarlo de la tabla de INSCRIPCIONES o en todo caso modificar la fecha de la inscripción o del nacimiento.

Lo mismo ocurriría en el caso de ventas y cobranzas. Primero se hace la venta y después la cobranza, sería muy raro el caso inverso.

Lo correcto es que siempre cuando de borrar se trata lo hagamos tomando en cuenta los identificadores de las tablas porque así disminuye mucho la probabilidad de equivocarnos y borrar filas que no deberían ser borradas.

Pero en situaciones como las antedichas, en que por un error de programación se permitió inscribir a un alumno antes de que naciera o se realizó una cobranza antes de una venta, podríamos necesitar borrar esas filas erróneas.

Una posible solución es la siguiente:

DELETE FROM
   INSCRIPCIONES I
WHERE
   I.INS_FECHAX < (SELECT A.ALU_FECNAC FROM ALUMNOS A WHERE I.INS_IDEALU = A.ALU_IDENTI)

En este caso borramos de la tabla de INSCRIPCIONES todas las filas donde la fecha de inscripción sea menor que la fecha de nacimiento del respectivo alumno.

INS_FECHAX es la fecha de la inscripción

ALU_FECNAC es la fecha de nacimiento del alumno

INS_IDEALU es el identificador del alumno en la tabla de INSCRIPCIONES

ALU_IDENTI es el identificador del alumno en la tabla de ALUMNOS

Artículo relacionado:

El índice del blog Firebird21

Borrando filas de detalle en Maestro/detalle

1 comentario

Si tenemos dos tablas: Maestro y Detalle, podemos determinar lo que ocurrirá cuando borramos (o intentamos borrar) una fila del Maestro según lo que hayamos especificado en la Foreign Key, tal como se explica en este artículo:

https://firebird21.wordpress.com/2013/05/26/entendiendo-a-las-foreign-keys/

Sin embargo, a veces necesitamos borrar las filas del Detalle cuando las filas del Maestro cumplen con alguna condición. Y como la Foreign Key no tiene la regla de CASCADE para borrado, no nos sirve borrar la fila del Maestro.

Por ejemplo, queremos borrar las filas de detalle cuando:

  • La fecha de la venta es 26/JUN/2013
  • El identificador del cliente es 12345
  • La moneda es dólares americanos

Para estos casos es muy útil el operador IN, como vemos a continuación:

DELETE FROM
   VENTASDET D
WHERE
   D.VEN_IDECAB IN (SELECT C.VTC_IDENTI FROM VENTASCAB C WHERE C.VTC_IDECLI = 12345)

VENTASDET es la tabla de detalles de las ventas

VENTASCAB es la tabla cabecera (o maestro) de las ventas

VEN_IDECAB es la columna de VENTASDET donde se guarda el identificador de la cabecera

VTC_IDENTI es el identificador de la cabecera

VTC_IDECLI es el identificador del cliente

Este comando DELETE borrará los detalles de todas las ventas que se le hicieron al cliente que tiene identificador 12345. Luego, si es necesario habría que escribir:

DELETE FROM VENTASCAB WHERE VTC_IDECLI = 12345

para borrar también las filas de cabecera de las ventas realizadas a ese cliente.

Desde luego que la forma más fácil y sencilla de conseguir esto es que la Foreign Key sea ON DELETE CASCADE para que al borrar una fila del maestro se borren todas las correspondientes filas de detalle. Pero si la Foreign Key no es ON DELETE CASCADE y no podemos cambiarla entonces aquí se mostró una posible solución al problema de borrar filas de detalles cuando las filas de cabecera cumplen con una condición.

Artículo relacionado:

El índice del blog Firebird21

Insertar, modificar o borrar filas en una Base de Datos externa

10 comentarios

En general, lo recomendable es que todas tus tablas estén en una sola Base de Datos ya que así puedes relacionar muy fácilmente a esas tablas entre sí. Sin embargo a veces no puedes evitar tener dos o más bases de datos. En ese caso: ¿cómo harías para insertar, modificar o borrar filas en una tabla que se encuentra en la otra Base de Datos?

El comando EXECUTE STATEMENT viene rápidamente en tu ayuda.

CREATE PROCEDURE INSERTAR_EN_BASEDATOS_EXTERNA_1
AS
   DECLARE VARIABLE lcComando VARCHAR(200);
   DECLARE VARIABLE lnIdenti  BIGINT;
   DECLARE VARIABLE lcNombre  VARCHAR(40);
BEGIN

   lnIdenti = 0;
   lcNombre = '''Esta es una prueba''';

   lcComando = 'INSERT INTO TARJETAS(TAR_IDENTI, TAR_NOMBRE) VALUES(' ||
               :lnIdenti || ',' || :lcNombre || ')';

   EXECUTE STATEMENT
      lcComando
   ON EXTERNAL
      'E:\BASESDATOS\CONTABILIDAD.FDB'
   AS
      USER 'SYSDBA' PASSWORD 'masterkey';
END;

Como puedes ver la variable lcNombre está rodeada por tres apóstrofos ¿por qué eso? porque si no se hace así ocurre un error ya que el contenido de esa variable debe estar rodeado de apóstrofos en el comando INSERT INTO. Cuando quieres que dentro de un string aparezca un apóstrofo debes escribir dos apóstrofos seguidos. Por ese motivo tuve que escribir tres apóstrofos.

Quizás también hayas notado que aunque la variable lnIdenti es numérica se la concatenó sin necesidad de convertirla previamente a tipo carácter, esa es una gran facilidad que nos da el Firebird.

En este ejemplo, los valores que insertamos en las columnas TAR_IDENTI y TAR_NOMBRE son constantes, sin embargo lo más común es que necesitemos insertar valores variables, como se ve en el siguiente ejemplo:

CREATE PROCEDURE INSERTAR_EN_BASEDATOS_EXTERNA_2(
   tnIdenti BIGINT,
   tcNombre VARCHAR(40))
AS
   DECLARE VARIABLE lcComando VARCHAR(200);
BEGIN

   lcComando = 'INSERT INTO TARJETAS(TAR_IDENTI, TAR_NOMBRE) VALUES(' ||
               :tnIdenti || ',''' || :tcNombre || ''')';

   EXECUTE STATEMENT
      lcComando
   ON EXTERNAL
      'E:\BASESDATOS\CONTABILIDAD.FDB'
   AS
      USER 'SYSDBA' PASSWORD 'masterkey';

END;

En este ejemplo los datos a insertar en la tabla externa fueron enviados como parámetros al stored procedure. Fíjate bien como el parámetro :tcNombre está rodeado por apóstrofos. Debes escribir esos apóstrofos para que funcione.

 Conclusión:

Para poder insertar, modificar o borrar filas en una tabla que se encuentra en otra Base de Datos debemos crear un string y luego ejecutarlo con el comando EXECUTE STATEMENT. Hay que tener en cuenta que las variables de tipo carácter deben estar rodeadas por apóstrofos.

Artículo relacionado:

https://firebird21.wordpress.com/2013/04/18/execute-statement/

Uso de la Primary Key

2 comentarios

Como sabes, una Primary Key es una clave que tiene estas dos características:

  • Sus valores son únicos (jamás se pueden repetir)
  • Sus valores no pueden ser NULL

Eso nos permite identificar a cada fila (a cada registro) de una forma unívoca, es decir con la total seguridad de que estamos identificando a la fila correcta.

¿Cuándo deberíamos utilizar una Primary Key?

1. Cuando deseamos consultar la tabla en el mismo orden en que las filas fueron cargadas

Por ejemplo, escribiendo:

SELECT
   PRD_IDENTI,     -- Identificador y Primary Key
   PRD_NOMBRE,     -- Nombre del producto
   PRD_PRECTO      -- Precio de costo del producto
FROM
   PRODUCTOS
ORDER BY
   PRD_IDENTI

2. Cuando hacemos búsquedas y queremos asegurarnos de obtener las filas correctas, sin la posibilidad de equivocarnos. Por ejemplo, supongamos que queremos borrar de la tabla de PRODUCTOS al producto cuyo identificador es 12345 y cuyo nombre es ‘Jugo de naranjas’. Si escribimos:

DELETE FROM
   PRODUCTOS
WHERE
   PRD_IDENTI = 12345

estaremos seguros de borrar a un solo producto, el que tiene como identificador al número 12345.

En cambio, si escribimos algo como:

DELETE FROM
   PRODUCTOS
WHERE
   PRD_PROCED = 'Japón'

Estaríamos borrando a todos los productos cuyo país de procedencia es Japón. Podríamos creer que hay un solo producto que tiene esa procedencia pero en realidad hay varios y en ese caso los estaríamos borrando a todos, lo cual sería un error grave.

Por ese motivo, en general, cuando de borrar se trata deberíamos usar la Primary Key, es más seguro.