Crear una tabla e insertarle filas DENTRO de un stored procedure

Deja un comentario

Casi siempre lo recomendable es que todas las tablas las crees afuera de tus stored procedures porque de esa manera el Firebird puede detectar cualquier error que cometas antes de que la tabla sea creada, y eso te evitará problemas futuros.

Sin embargo, a veces puedes necesitar crear una tabla adentro de un stored procedure y también insertarle datos a esa tabla.

La técnica para conseguirlo es usar el comando EXECUTE STATEMENT con la opción WITH AUTONOMOUS TRANSACTION, como puedes ver en el siguiente ejemplo:

CREATE PROCEDURE CREAR_E_INSERTAR
AS
   DECLARE VARIABLE lcNombreTabla VARCHAR(31);
   DECLARE VARIABLE lcComando     VARCHAR(32000);
BEGIN

   lcNombreTabla = 'PRUEBA1';

   lcComando = 'CREATE TABLE ' || lcNombreTabla || ' (PRU_IDENTI INTEGER, PRU_NOMBRE VARCHAR(80))' ;

   EXECUTE STATEMENT :lcComando WITH AUTONOMOUS TRANSACTION;

   lcComando = 'INSERT INTO ' || lcNombreTabla || '(PRU_IDENTI, PRU_NOMBRE) VALUES (1, ''AMÉRICA'')';

   EXECUTE STATEMENT :lcComando WITH AUTONOMOUS TRANSACTION;

   lcComando = 'INSERT INTO ' || lcNombreTabla || '(PRU_IDENTI, PRU_NOMBRE) VALUES (2, ''ÁFRICA'')';

   EXECUTE STATEMENT :lcComando WITH AUTONOMOUS TRANSACTION;

   lcComando = 'INSERT INTO ' || lcNombreTabla || '(PRU_IDENTI, PRU_NOMBRE) VALUES (3, ''ASIA'')';

   EXECUTE STATEMENT :lcComando WITH AUTONOMOUS TRANSACTION;

   lcComando = 'INSERT INTO ' || lcNombreTabla || '(PRU_IDENTI, PRU_NOMBRE) VALUES (4, ''EUROPA'')';

   EXECUTE STATEMENT :lcComando WITH AUTONOMOUS TRANSACTION;

   lcComando = 'INSERT INTO ' || lcNombreTabla || '(PRU_IDENTI, PRU_NOMBRE) VALUES (5, ''OCEANÍA'')';

   EXECUTE STATEMENT :lcComando WITH AUTONOMOUS TRANSACTION;

END;

Aquí, se crea una tabla llamada PRUEBA1, la cual está compuesta por dos columnas: PRU_IDENTI y PRU_NOMBRE. Luego de crear la tabla se le insertan 5 filas. El resultado de ejecutar este stored procedure es el siguiente:

CREAR1

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

En la Captura 1 podemos ver que se ha creado la tabla llamada PRUEBA1, con las columnas que habíamos especificado.

CREAR2

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

Y en la Captura 2 vemos que también se le insertaron las filas que habíamos pedido que se inserten.

Comentarios:

Al utilizar WITH AUTONOMOUS TRANSACTION el efecto es ejecutar el comando COMMIT dentro de nuestro stored procedure. Por defecto el Firebird no nos permite escribir ni COMMIT ni ROLLBACK dentro de un stored procedure pero podemos engañarle y ejecutar un COMMIT cuando llamamos al comando EXECUTE STATEMENT con la opción WITH AUTONOMOUS TRANSACTION.

Hay que recordar, sin embargo, que utilizar EXECUTE STATEMENT … WITH AUTONOMOUS TRANSACTION tiene sus desventajas:

  1. El Firebird no verifica que el comando a ejecutar sea correcto antes de ejecutarlo. La verificación siempre se hace después y si hay algo mal escrito entonces solamente se descubrirá en tiempo de ejecución (quizás por un usuario malhumorado).
  2. Usar EXECUTE STATEMENT es lento porque el comando no está compilado, debe ser compilado en el momento de ser ejecutado

 Si luego de sopesar las ventajas y desventajas decides que necesitas un COMMIT dentro de un stored procedure entonces recuerda que lo conseguirás usando AUTONOMOUS TRANSACTION.

Artículos relacionados:

EXECUTE STATEMENT

Usando EXECUTE STATEMENT con argumentos

El índice del blog Firebird21

 

 

 

Creando tablas agregadas e insertándoles datos

3 comentarios

En el artículo:

https://firebird21.wordpress.com/2013/04/28/tablas-agregadas/

habíamos visto lo que son las tablas agregadas y la gran utilidad que podemos obtener de ellas. En éste veremos una forma de crearlas (no la única forma, sino una forma) y también como insertarles datos.

Necesitaremos escribir dos stored procedures para conseguir nuestro objetivo. ¿Por qué? porque el Firebird no nos permite tener un COMMIT dentro de un stored procedure y no podemos insertarle filas a una tabla antes de hacerle un COMMIT exitoso, por lo tanto deberemos crear dos stored procedures:

  • Uno para crear la tabla y su Primary Key
  • Otro para insertarle filas a esa tabla

Ejemplo:

Tenemos una tabla llamada PRODUCTOS, en la cual se guardan el Identificador de cada producto, su Nombre y otros datos. Una tabla llamada MOVIMCAB (cabecera de movimientos) en la cual se guardan el Identificador del movimiento, la Fecha en que ocurrió, el Tipo de Documento que lo avala, el Número del Documento y otros datos. Y una tabla llamada MOVIMDET (detalles de movimientos) en la cual se guarda el Identificador del detalle, el Identificador de la cabecera, el Identificador del producto, la Cantidad vendida y otros datos.

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

En la Captura 1. vemos el Identificador y el Nombre de algunos productos.

AGRUPADAS2

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

Y en la Captura 2. vemos el Identificador del detalle, el Identificador de la cabecera, el Identificador del producto y la Cantidad vendida.

Y lo que queremos obtener es lo siguiente:

AGRUPADAS3

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

En la columna TAB_IDENTI se guarda el Identificador de la cabecera, y en las demás columnas la Cantidad vendida de cada uno de esos productos. Para hacer el ejemplo simple no se agregaron más columnas pero si tú quieres además del Identificador de la cabecera podrías agregarle la Fecha de la Venta, el Tipo de Documento, el Número del Documento, el Nombre del Cliente, etc., o sea que cualquier columna de la tabla MOVIMCAB o sus referenciadas podrías agregar.

Un stored procedure para crear la tabla agregada

CREATE PROCEDURE CREAR_TABLA_AGREGADA(
   tcNombreTabla VARCHAR(32))
AS
   DECLARE VARIABLE lcCreate   VARCHAR(4096);
   DECLARE VARIABLE lcColumna  VARCHAR(28);
BEGIN

 -- Primero, creamos la tabla

   lcCreate = 'CREATE TABLE ' || tcNombreTabla || '(';

   lcCreate = lcCreate || 'TAB_IDENTI INTEGER NOT NULL, ';

   FOR SELECT
      DISTINCT
      PRD_NOMBRE AS MOV_NOMPRD
   FROM
      MOVIMDET
   JOIN
      PRODUCTOS
         ON MOV_IDEPRD = PRD_IDENTI
   ORDER BY
      MOV_IDECAB
   INTO
      :lcColumna
   DO BEGIN
      lcColumna = Left(lcColumna, 28);
      lcColumna = Replace(lcColumna, ' ', '_');
      lcColumna = Replace(lcColumna, '.', '_');
      lcColumna = Replace(lcColumna, '/', '_');
      lcCreate = lcCreate || lcColumna || ' VARCHAR(28), ' ;
   END

   lcCreate = Left(lcCreate, Char_Length(lcCreate) - 2);

   lcCreate = lcCreate || ');';

   EXECUTE STATEMENT lcCreate;

 -- Segundo, le agregamos una Primary Key

   EXECUTE STATEMENT 'ALTER TABLE ' || tcNombreTabla || ' ADD CONSTRAINT PK_' || tcNombreTabla || ' PRIMARY KEY (TAB_IDENTI)';

END;

Este stored procedure crea una tabla con el nombre que quieras asignarle y también una Primary Key para esa tabla. Fíjate que se usa DISTINCT porque los nombres de las columnas no pueden estar repetidos. Y el nombre de la columna se obtiene editando el Nombre del Producto y reemplazando algunos caracteres por guiones bajos. Por supuesto que si no te gustan los guiones bajos podrías usar otros caracteres para el reemplazo.

Un ejemplo de llamada sería el siguiente:

EXECUTE PROCEDURE CREAR_TABLA_AGREGADA('PRUEBA2');


AGRUPADAS5

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

Y así hemos creado una tabla que tiene columnas con ¡¡¡los nombres de los productos!!!. Muy interesante, ¿verdad?. Desde luego que en tu caso podrías poner nombres de alumnos, de profesores, de clientes, de países, de lo que se te ocurra.

Un stored procedure para insertarle filas a la tabla agregada

CREATE PROCEDURE ACTUALIZAR_TABLA_AGREGADA(
   tcNombreTabla VARCHAR(32))
AS
   DECLARE VARIABLE lcUpdate VARCHAR(4096);
   DECLARE VARIABLE lcColumna VARCHAR(28);
   DECLARE VARIABLE lnIdenti INTEGER;
   DECLARE VARIABLE lnCantidad SMALLINT;
BEGIN

   FOR SELECT
      MOV_IDECAB,
      PRD_NOMBRE,
      SUM(MOV_CANTID)
   FROM
      MOVIMDET
   JOIN
      PRODUCTOS
         ON MOV_IDEPRD = PRD_IDENTI
   GROUP BY
      MOV_IDECAB,
      PRD_NOMBRE
   INTO
      :lnIdenti,
      :lcColumna,
      :lnCantidad
   DO BEGIN
      lcColumna = Left(lcColumna, 28);
      lcColumna = Replace(lcColumna, ' ', '_');
      lcColumna = Replace(lcColumna, '.', '_');
      lcColumna = Replace(lcColumna, '/', '_');
      lcUpdate = 'UPDATE OR INSERT INTO ' ||
                 tcNombreTabla || ' (' ||
                 'TAB_IDENTI, ' || lcColumna || ') VALUES (' ||
                 lnIdenti || ', '||
                 lnCantidad || ')';
      EXECUTE STATEMENT lcUpdate;
   END

END;

Aquí se editó el Nombre del Producto de la misma forma que habíamos hecho en el stored procedure CREAR_TABLA_AGREGADA porque los nombres de las columnas deben coincidir. Para poder usar el comando UPDATE OR INSERT se requiere de una Primary Key y por eso la tabla tiene una Primary Key. Este stored procedure debe recibir como parámetro de entrada el mismo nombre que se usó para crear la tabla agregada. Por ejemplo:

EXECUTE PROCEDURE ACTUALIZAR_TABLA_AGREGADA('PRUEBA2')

AGRUPADAS3

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

Y listo, eso es todo. Escribiendo dos stored procedures similares a los mostrados arriba podrás tener todas las tablas agregadas que quieras. Como recordarás, lo que generalmente se coloca en las tablas agregadas son agrupamiento de datos (sumas, cantidades totales, promedios, máximos, mínimos, etc.) porque sirven para analizar datos, no para verificar datos.

Artículos relacionados:

Tablas agregadas

Creando tablas dentro de un stored procedure o de un trigger

El índice del blog Firebird21

Creando tablas dentro de un stored procedure o de un trigger

2 comentarios

Como seguramente sabes, lo normal y generalmente lo mejor es que crees tablas afuera de los stored procedures y de los triggers. ¿Por qué? porque de esa manera es el propio Firebird quien se encargará de verificar que todo esté correcto en tiempo de compilación, o sea cuando los usuarios aún no están usando la Base de Datos. Si él descubre algún error (por ejemplo quisiste escribir INTEGER pero escribiste INTEGGGGER) te mostrará un mensaje adecuado y no te permitirá grabar la estructura de esa tabla. O sea que en tiempo de compilación ya descubrió el error y te lo señaló. Y eso está muy bien.

Sin embargo, a veces puedes necesitar crear tablas cuyas características desconoces en tiempo de compilación y solamente las puedes conocer en tiempo de ejecución (o sea cuando los usuarios ya están usando la Base de Datos). El problema es que si escribiste algo mal recién te enterarás en tiempo de ejecución.

Para crear una tabla en tiempo de ejecución utilizaremos el comando EXECUTE STATEMENT, como vemos a continuación:

CREATE PROCEDURE CREAR_TABLA
AS
   DECLARE VARIABLE lcComando VARCHAR(4096);
BEGIN

   lcComando = 'CREATE TABLE PRUEBA1 (
                   PRU_NUMERO INTEGER,
                   PRU_NOMBRE VARCHAR(40),
                   PRU_CANTID SMALLINT);' ;

   EXECUTE STATEMENT lcComando;

END;

Como puedes ver, el truco está en escribir un comando que se encargue de crear la tabla. Desde luego que el comando debe ser sintáticamente correcto, ya que si hay un error (escribiste INTEGGGER en lugar de INTEGER) la tabla no será creada y verás un mensaje similar al siguiente:

Invalid command.
Specified domain or source column INTEGGGER does not exist.
At procedure ‘CREAR_TABLA’ line: 11, col: 4.

Pero si escribiste todo bien podrás crear todas las tablas que quieras en tiempo de ejecución. Y eso puede ser muy útil en ciertos casos.

Además, no solamente puedes crear tablas, también si lo deseas puedes alterar tablas, borrar tablas, crearles índices, Primary Keys, Foreign Keys, etc.

CREATE PROCEDURE BORRAR_TABLA
AS
   DECLARE VARIABLE lcComando VARCHAR(1024);
BEGIN

   lcComando = 'DROP TABLE PRUEBA1;' ;

   EXECUTE STATEMENT lcComando;

END;

El stored procedure BORRAR_TABLA se encarga de borrar la tabla PRUEBA1, también si quisiéramos podríamos hacerlo más general, para que pueda borrar a cualquier tabla, no solamente a PRUEBA1. En ese caso escribiríamos algo como:

CREATE PROCEDURE BORRAR_TABLA(
   tcNombreTabla VARCHAR(32))
AS
   DECLARE VARIABLE lcComando VARCHAR(1024);
BEGIN

   lcComando = 'DROP TABLE ' || tcNombreTabla || ';' ;

   EXECUTE STATEMENT lcComando;

END;

Y lo ejecutaríamos de esta manera:

EXECUTE PROCEDURE BORRAR_TABLA('PRUEBA1');

Donde el stored procedure recibe como parámetro de entrada el nombre de la tabla que se desea borrar.

Manejo de errores

Si no eres cuidadoso varios errores pueden ocurrir, por ejemplo: quieres crear la tabla PRUEBA1 pero ya existe la tabla PRUEBA1. O quieres borrar la tabla PRUEBA1 pero no existe una tabla con ese nombre. O cuando creas una tabla su estructura está mal. O muchos otros.

Debes estar prevenido ante esas posibilidades y en tu stored procedure o trigger prever todas las posibilidades y usar excepciones para manejar los errores.

Artículos relacionados:

Entendiendo las excepciones

El índice del blog Firebird21

Usando EXECUTE STATEMENT con argumentos

Deja un comentario

El comando EXECUTE STATEMENT puede ser muy útil para realizar muchas tareas porque nos permite armar la instrucción que necesitamos ejecutar de muchas maneras. El tema es: cuando la instrucción que queremos ejecutar requiere de variables ¿cómo la armamos?

Tenemos tres posibilidades:

  1. Concatenando los parámetros
  2. Usando parámetros con nombres
  3. Usando parámetros por posición

Ejemplo:

(Este es un ejemplo muy sencillo y hay mejores formas de resolverlo pero sirve para demostrar como usar EXECUTE STATEMENT con parámetros)

Queremos crear un stored procedure que nos muestre los productos cuyos precios de venta están dentro del rango especificado.

Esta es nuestra tabla de PRODUCTOS:

EXECUTE1

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

1. Concatenando los parámetros

CREATE PROCEDURE EXECUTE1(
   tnPrecio1 INTEGER,
   tnPrecio2 INTEGER)
RETURNS(
   ftcNombre VARCHAR(40),
   ftnPrecio INTEGER)
AS
   DECLARE VARIABLE lcComando VARCHAR(1000);
BEGIN

  lcComando = 'SELECT
                  PRD_NOMBRE,
                  PRD_PREVTA
               FROM
                  PRODUCTOS
               WHERE
                  PRD_PREVTA >= ' || tnPrecio1 || ' AND
                  PRD_PREVTA <= ' || tnPrecio2;

   FOR EXECUTE STATEMENT
      lcComando
   INTO
      :ftcNombre,
      :ftnPrecio
   DO BEGIN
      SUSPEND;
   END

END;

Como este es un stored procedure seleccionable para ejecutarlo debemos escribir algo como:

SELECT * FROM EXECUTE1(3000, 5000)

y este será el resultado que obtendremos:

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

2. Usando parámetros con nombres

CREATE PROCEDURE EXECUTE2(
   tnPrecio1 INTEGER,
   tnPrecio2 INTEGER)
RETURNS(
   ftcNombre VARCHAR(40),
   ftnPrecio INTEGER)
AS
   DECLARE VARIABLE lcComando VARCHAR(1000);
BEGIN

   lcComando = 'SELECT
                   PRD_NOMBRE,
                   PRD_PREVTA
                FROM
                   PRODUCTOS
                WHERE
                   PRD_PREVTA >= :lnPrecio1 AND
                   PRD_PREVTA <= :lnPrecio2';

   FOR EXECUTE STATEMENT
      (lcComando) (lnPrecio1 := tnPrecio1, lnPrecio2 := tnPrecio2)
   INTO
      :ftcNombre,
      :ftnPrecio
   DO BEGIN
      SUSPEND;
   END

END;

En este caso dentro de lcComando usamos dos variables locales llamadas lnPrecio1 y lnPrecio2. Pero esas variables no es necesario declararlas después del AS (puedes declararlas si quieres, pero no es necesario).

Como estamos usando parámetros con nombres, no importa el orden en el cual especificamos los parámetros. O sea que ambas de estas formas son equivalentes, podemos usar cualquiera de ellas:

FOR EXECUTE STATEMENT
   (lcComando) (lnPrecio1 := tnPrecio1, lnPrecio2 := tnPrecio2)

FOR EXECUTE STATEMENT
   (lcComando) (lnPrecio2 := tnPrecio2, lnPrecio1 := tnPrecio1)

Como puedes ver nuestros parámetros tienen nombres (en este ejemplo: lnPrecio1 y lnPrecio2)

3. Usando parámetros por posición

CREATE PROCEDURE EXECUTE3(
   tnPrecio1 INTEGER,
   tnPrecio2 INTEGER)
RETURNS(
   ftcNombre VARCHAR(40),
   ftnPrecio INTEGER)
AS
   DECLARE VARIABLE lcComando VARCHAR(1000);
BEGIN

   lcComando = 'SELECT
                   PRD_NOMBRE,
                   PRD_PREVTA
                FROM
                   PRODUCTOS
                WHERE
                   PRD_PREVTA >= ? AND
                   PRD_PREVTA <= ?';

   FOR EXECUTE STATEMENT
      (lcComando) (tnPrecio1, tnPrecio2)
   INTO
      :ftcNombre,
      :ftnPrecio
   DO BEGIN
      SUSPEND;
   END

END;

En este caso, el primer signo de interrogación dentro de lcComando es reemplazado por el primer parámetro enviado. El segundo signo de interrogación por el segundo parámetro y así sucesivamente. Por lo tanto aquí sí importa el orden en el cual enviamos los parámetros. Si enviamos los parámetros con el orden equivocado (es decir: primero tnPrecio2 y después tnPrecio1) entonces obtendremos un resultado erróneo.

Conclusión:

Poder enviarle parámetros al comando que ejecutamos con EXECUTE STATEMENT es muy útil y nos permite escribir código fuente más confiable.

En general es preferible enviar parámetros con nombres porque eso disminuye la probabilidad de cometer errores. Además, si un parámetro se usará varias veces dentro de lcComando con enviarlo una sola vez será suficiente.

La ventaja de enviar los parámetros por posición es que se escribe menos. La desventaja es que si equivocamos el orden de los parámetros enviados el resultado que obtendremos será erróneo. Y si un parámetro se necesita varias veces tendremos que enviarlo varias veces.

La opción de concatenar los parámetros es la menos recomendable porque cuando lcComando es largo y recibe varios parámetros tantas concatenaciones pueden marear a cualquiera

Artículos relacionados:

EXECUTE STATEMENT

El índice del blog Firebird21

Consultas a bases de datos externas

9 comentarios

A veces en nuestra Base de Datos actual no están todos los datos que necesitamos pues algunos se encuentran en otras bases de datos. ¿Podemos consultar esas otras bases de datos?

La respuesta es sí y la forma de hacerlo es mediante el comando EXECUTE STATEMENT. Sin embargo hay dos puntos importantes que debes recordar:

  1. El Firebird no valida que el EXECUTE STATEMENT sea correcto antes de ejecutarlo, por lo tanto eres tú quien debe asegurarse de ello
  2. El EXECUTE STATEMENT es lento (a veces puede ser demasiado lento)

Los dos puntos anteriores implican que debes sopesar bien si vale la pena usar el EXECUTE STATEMENT o si mejor sería pensar en otra alternativa.

Ejemplo:

En una Base de Datos llamada ERP2000.FDB tenemos una tabla llamada CARGOS que tiene estas filas:

EXTERNAS1

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

La columna CAR_IDENTI es el identificador del cargo, se usa internamente para referenciar a otras tablas con esta tabla. La columna CAR_CODIGO es la que se le muestra a los usuarios, ellos nunca ven la columna CAR_IDENTI ¿por qué? porque no necesitan verla, es para uso interno.

En otra Base de Datos, llamada BLOG_DB.FDB queremos ver las filas de la tabla CARGOS de ERP2000.FDB, para eso en BLOG_DB.FDB escribimos el siguiente stored procedure seleccionable:

SET TERM ^ ;

CREATE PROCEDURE OBTENER_CARGOS
RETURNS(
   ftnCodSuc SMALLINT,
   ftnIdenti INTEGER,
   ftcCodigo CHAR(4),
   ftcNombre VARCHAR(25))
AS
   DECLARE VARIABLE lcComando VARCHAR(256);
BEGIN

   lcComando = 'SELECT CAR_CODSUC, CAR_IDENTI, CAR_CODIGO, CAR_NOMBRE FROM CARGOS' ;

   FOR EXECUTE STATEMENT
      lcComando
   ON EXTERNAL
      'E:\SQL\SQL_CONTA\DATABASES\ERP2000.FDB'
   AS
      USER     'SYSDBA'
      PASSWORD 'masterkey'
   INTO
      :ftnCodSuc,
      :ftnIdenti,
      :ftcCodigo,
      :ftcNombre
   DO BEGIN
      SUSPEND;
   END

END^

SET TERM ; ^

Y después de escribir este comando:

SELECT * FROM OBTENER_CARGOS

este será el resultado que obtendremos:

EXTERNAS2

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

donde, como puedes ver, están todas las filas de la tabla CARGOS aunque los nombres de las columnas difieren porque los nombres de ellas son los que elegimos como parámetros de salida en nuestro stored procedure.

Conclusión:

Es perfectamente posible consultar bases de datos externas con Firebird pero como esa consulta se realiza con el comando EXECUTE STATEMENT a veces obtener los resultados puede demorar mucho tiempo por lo tanto debes verificar que la velocidad de respuesta esté dentro de los límites aceptables. En general si la tabla externa no tiene demasiadas filas no habría problemas. “Demasiadas filas” depende de tu hardware y de tu software, así que debes verificar.

Artículos relacionados:

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

Importar datos desde otra Base de Datos

El índice del blog Firebird21

Un stored procedure para conocer la cantidad de filas de todas las tablas

1 comentario

Como seguramente ya sabrás, usar la función COUNT(*) en tablas que tienen muchas filas no es una buena idea, porque como el Firebird no guarda la cantidad de filas que tiene cada tabla en algún lado, lo que hace la función COUNT() es recorrer la tabla desde el principio hasta el fin y contar cuantas filas hay en total.

Y claro que en tablas pequeñas, que tienen solamente algunas miles de filas no hay problema porque la función COUNT() trabaja muy rápido, pero en tablas grandes de millones y millones de filas es otra cosa, allí la espera puede ser desesperante.

Por eso, el siguiente stored procedure solamente es recomendable de ejecutar cuando todas las tablas de tu Base de Datos tienen pocas filas, o en casos excepcionales cuando necesitas conocer la cantidad de filas de cada tabla.

Este stored procedure seleccionable te mostrará, para cada tabla de tu Base de Datos, la cantidad de filas que tiene.

SET TERM ^ ;
CREATE PROCEDURE CANTIDAD_FILAS
RETURNS(
   NOMBRETABLA VARCHAR(32),
   FILAS INTEGER)
AS
DECLARE VARIABLE COMANDO VARCHAR(80);
BEGIN

 FOR SELECT RDB$RELATION_NAME
       FROM RDB$RELATIONS
      WHERE RDB$FLAGS = '1'
       INTO :NombreTabla
       DO BEGIN
          Comando = 'SELECT COUNT(*) FROM ' || :NombreTabla;
          EXECUTE STATEMENT COMANDO
          INTO :Filas ;
          SUSPEND;
       END

END^

SET TERM ; ^

Para ejecutarlo, escribe lo siguiente:

SELECT
   *
FROM
   CANTIDAD_FILAS