Agregando filas adicionales (2)

8 comentarios

Ya hemos visto en este artículo:

Agregando filas adicionales

como podemos hacer para agregar filas al resultado de salida de nuestra consulta. Para ello escribimos un stored procedure seleccionable.

Ahora, veremos otra técnica para obtener lo que deseamos, usando una construcción del Firebird llamada CTE (Common Table Expresion).

En nuestro ejemplo, la salida tiene 18 filas. ¿Por qué? porque hay 2 filas para Países, 4 filas para Estados y 12 filas para Ciudades. En total, 18 filas.

Y esas son exactamente las filas que tiene cada una de las tablas.

Eso nos trae a la mente que podemos usar el comando UNION para unir el conjunto resultado de la tabla PAÍSES con el conjunto resultado de la tabla ESTADOS y con el conjunto resultado de la tabla CIUDADES.

Listado 1.

WITH MiUnion AS (

   SELECT
      PAI_IDENTI * 1000000 AS CIU_ORDENX,
      PAI_NOMBRE           AS CIU_NOMPAI,
      ''                   AS CIU_NOMEST,
      ''                   AS CIU_NOMBRE
   FROM
      PAISES

   UNION

   SELECT
      EST_IDEPAI * 1000000 + EST_IDENTI * 1000 AS CIU_ORDENX,
      ''                                       AS CIU_NOMPAI,
      EST_NOMBRE                               AS CIU_NOMEST,
      ''                                       AS CIU_NOMBRE
   FROM
      ESTADOS

   UNION

   SELECT
      CIU_IDEPAI * 1000000 + CIU_IDEEST * 1000 + CIU_IDENTI AS CIU_ORDENX,
      ''                                                    AS CIU_NOMPAI,
      ''                                                    AS CIU_NOMEST,
      CIU_NOMBRE
   FROM
      CIUDADES

)

SELECT
   *
FROM
   MiUnion
ORDER BY
   CIU_ORDENX

Y este es el resultado que obtenemos al ejecutar el Listado 1.

CIUDADES10

 

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

Desde luego que la columna CIU_ORDENX no es necesario mostrarla a los usuarios, se muestra en la Captura 1. para que sea más fácil entender la técnica utilizada.

Explicación:

El comando UNION nos permite agregar al resultado de una consulta el resultado de otra consulta, obteniendo así un conjunto resultado que es la suma de los anteriores.

Como nuestras tablas tienen 2 filas, 4 filas y 12 filas, al hacer las UNIONes entre ellas el resultado final tendrá sí o sí, 18 filas.

La tabla MiUnion es una tabla virtual, una tabla que solamente existe en la memoria de la computadora, o sea, es una tabla CTE.

Como MiUnion es una tabla virtual puede tener todas las columnas que se nos ocurra, inclusive podemos inventarle columnas, tal como hicimos con la columna CIU_ORDENX.

La columna CIU_ORDENX utilizamos para guardar en ella el orden en el cual deseamos que las filas sean mostradas. Aquí obtenemos sus valores en potencias de 1.000 porque suponemos que las cantidades de Países, de Estados y de Ciudades serán menos que 1.000 y que sus identificadores respectivos también siempre serán menores que 1.000. Si cualquiera de ellos pueden ser más que 1.000 entonces tendríamos que usar potencias de 10.000, de 100.000, etc.

CIU_ORDENX para un País es: Identificador_del_País * 1.000 * 1.000

CIU_ORDENX para un Estado es: Identificador_del_País * 1.000 * 1.000 + Identificador_del_Estado * 1.000

CIU_ORDENX para una Ciudad es: Identificador_del_País * 1.000 * 1.000 + Identificador_del_Estado * 1.000 + Identificador_de_la_Ciudad

Esto implica que CIU_ORDENX para un País siempre terminará con 6 ceros, para un Estado siempre terminará con 3 ceros y para una Ciudad siempre terminará entre 001 y 999.

También implica que la cantidad de Países no puede ser mayor que 999, que la cantidad de Estados no puede ser mayor que 999 y que la cantidad de Ciudades no puede ser mayor que 999. Si cualquiera de ellos pudiera ser mayor que 999 entonces en lugar de usar potencias de 1.000 habría que usar potencias de 10.000, de 100.000, etc.

Luego de haber creado a la tabla virtual MiUnion lo único que nos resta es mostrarla, ordenada según la columna virtual CIU_ORDENX, y así obtenemos el resultado deseado.

Artículos relacionados:

Usando CTE (Common Table Expresion)

Agregando filas adicionales

El índice del blog Firebird21

El foro del blog Firebird21

 

 

Anuncios

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

 

Extrayendo caracteres del WHERE

Deja un comentario

Lo correcto es no guardar en las tablas los datos con cualquier formato, sino guardarlos solamente con el formato que previamente hemos establecido.

Sin embargo a veces podríamos encontrarnos con la situación de que el formato es desconocido.

Ejemplo:

Un usuario podría escribir: 123-456-789

Otro usuario podría escribir: 1-23-456-7-89

Otro usuario podría escribir: 12-345678-9

Otro usuario podría escribir: 123456789

Entonces, ¿qué condición de filtro debemos escribir en el SELECT para que sea cual sea el formato podamos obtener el dato que nos interesa?

Listado 1.

SELECT
   *
FROM
   MiTabla
WHERE
   REPLACE(MiColumna, '-', '') = 'MiValorBuscado'

Comentario 1:

Aunque como hemos visto podemos escribir un SELECT que nos muestre todas las filas que tienen el valor buscado, hay un error intrínseco de diseño porque estamos permitiendo que los usuarios ingresen los datos como se les antoja, eso no debería ser así.

Para estos casos una posibilidad es tener un trigger BEFORE INSERT OR UPDATE que se encargue de extraer los guiones antes de guardar la fila en la tabla, algo como:

Listado 2.


New.MiColumna = REPLACE(New.MiColumna, '-', '')

Comentario 2:

Aunque el Listado 1. funcionará bien y nos mostrará lo que deseamos ver, si la tabla es muy grande puede llegar a ser muy lento, demorarse una eternidad. ¿Por qué? Porque no se usará un índice y reemplazar un carácter por otro no es instantáneo. Por ese motivo es preferible guardar la columna sin los guiones, como se muestra en el Listado 2. Pero … ¿y si necesitamos a veces mostrarle al usuario la columna exactamente igual a cómo él la había escrito?

Una solución es agregarle una columna a la tabla, y entonces tendremos dos columnas:

Una columna, para guardar en ella lo que escribió el usuario

Otra columna, para guardar en ella lo que escribió el usuario pero sin los guiones

Esta segunda columna podríamos obtenerla mediante un trigger similar al del Listado 2. o mediante una columna computada, algo como:

Listado 3.


MiColumnaEditada COMPUTED BY (REPLACE(New.MiColumna, '-', ''))

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

 

 

Obteniendo la primera fila de cada grupo

Deja un comentario

Como sabes, podemos usar la cláusula GROUP BY para agrupar las filas de una tabla. Sin embargo, a veces podríamos desear obtener solamente una fila de cada grupo.

Veamos un ejemplo.

PRIMERA_FILA_01

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

Como puedes ver, varias filas tienen el mismo identificador de la cabecera. No queremos obtener los totales de cada grupo, sino una fila por cada grupo, algo como:

PRIMERA_FILA_02

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

En la Captura 2. podemos ver el resultado que queremos conseguir, ¿cómo lo logramos?

Listado 1.

SELECT
   ASI_IDENTI,
   ASI_IDECAB,
   ASI_NUMCUE,
   ASI_MONTOX
FROM
   ASIENTOSDET D1
WHERE
   NOT EXISTS (SELECT
                  ASI_IDENTI
               FROM
                  ASIENTOSDET D2
               WHERE
                  D1.ASI_IDECAB = D2.ASI_IDECAB AND
                  D1.ASI_IDENTI > D2.ASI_IDENTI)

Para que la consulta mostrada en el Listado 1. sea rápida, debemos tener un índice según la columna ASI_IDECAB y otro índice según la columna ASI_IDENTI. Si no tenemos esos índices, esta consulta será lentísima en tablas que tienen muchas filas.

Listado 2.

SELECT
   T1.ASI_IDENTI,
   T1.ASI_IDECAB,
   T2.ASI_NUMCUE,
   T2.ASI_MONTOX
FROM
   (SELECT
       MIN(ASI_IDENTI) AS ASI_IDENTI,
       ASI_IDECAB
    FROM
       ASIENTOSDET
    GROUP BY
       ASI_IDECAB
   ) T1
JOIN
   ASIENTOSDET T2
      ON T1.ASI_IDENTI = T2.ASI_IDENTI

Haciendo pruebas el autor de este blog descubrió que la solución del Listado 1. es más eficiente que la solución del Listado 2. pero que en tablas no muy grandes (menos de 1.000.000 de filas) esa diferencia es imperceptible.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

 

Entendiendo WITH LOCK

2 comentarios

Este artículo es para expertos solamente.

Como seguramente ya sabes, en Firebird lo preferible y lo recomendable es que las transacciones sean optimistas. Sin embargo hay algunos casos en que se necesita que una transacción sea pesimista.

Estos casos son rarísimos y si los necesitas con frecuencia entonces evidentemente aún no has entendido como funciona Firebird.

Hay una cláusula del comando SELECT que te permite tener transacciones pesimistas pero si no entiendes muy pero muy bien como funcionan las transacciones en Firebird, no deberías usar esa cláusula jamás.

Este artículo es para expertos solamente.

Sintaxis

SELECT
   ...
FROM
   MiTabla
[WHERE
   MiCondición]
[FOR UPDATE [OF...]]
[WITH LOCK]

WITH LOCK realiza un bloqueo pesimista a la fila (o filas) del conjunto resultado del SELECT. Lo recomendable es que:

  1. La cantidad de filas bloqueadas sea muy pequeña (lo ideal es que sea una sola fila)
  2. La aplicación controle muy bien el bloqueo

Si usas WITH LOCK a veces conseguirás que las filas sean bloqueadas y a veces no. Si otra transacción ha previamente bloqueado a alguna de esas filas entonces WITH LOCK fallará.

Si WITH LOCK tuvo éxito entonces ninguna otra transacción podrá hacerle un UPDATE ni un DELETE a las filas involucradas, hasta que la transacción que hizo el WITH LOCK finalice. Por eso el bloqueo es pesimista.

Cuando se incluye la cláusula FOR UPDATE el bloqueo es aplicado a cada fila, una por una, a medida que son extraídas por el Servidor. Es por lo tanto posible (aunque muy improbable, pero posible) que un bloqueo que pareció tener éxito cuando se hizo el SELECT falle más adelante cuando se quiere hacer el UPDATE. ¿Por qué? Porque otra transacción concurrente bloqueó a esa fila e hizo un COMMIT antes de que el SELECT … WITH LOCK finalizara.

Esa es otra razón más para que el SELECT … WITH LOCK involucre a muy pocas filas (idealmente, a una sola fila). De esta manera se minimizará el riesgo.

Un SELECT … WITH LOCK te garantiza que:

  1. Todas las filas del conjunto resultado que obtuviste están bloqueadas
  2. Todas las demás filas no están bloqueadas

Un SELECT … WITH LOCK no te puede garantizar que:

  1. Todas las filas que cumplen con la condición del filtro del WHERE estén bloqueadas

Entendamos mejor este último punto.

Supongamos que si escribes tu SELECT … WHERE obtienes 100 filas, pero al escribir SELECT … WHERE … WITH LOCK obtienes 98 filas. Eso podría ocurrir si otra transacción concurrente ha bloqueado a las 2 filas faltantes entre el inicio y el fin del SELECT … WITH LOCK. Entonces el SELECT … WITH LOCK te garantiza que hay 98 filas bloqueadas pero no te garantiza que no hay filas que cumplen la condición de filtro del WHERE pero que no fueron bloqueadas (en este ejemplo, hay 2 filas que cumplen la condición del WHERE pero no están bloqueadas).

¿Recuerdas que lo ideal es que el SELECT … WITH LOCK devuelva una sola fila? Ahora se entiende mejor el motivo.

Bloqueo de filas

El SELECT … WITH LOCK realiza (si es que puede) un bloqueo pesimista pero siempre cumpliendo con los parámetros de las transacciones.

Eso significa que si el modo de bloqueo de la transacción donde se encuentra el SELECT … WITH LOCK es WAIT y otra transacción previamente ha bloqueado a una fila de su conjunto resultado, esperará hasta que la otra transacción finalice. Si su modo de bloqueo es NO WAIT entonces inmediatamente finalizará con error, y así con los demás parámetros.

¿Dónde no se puede usar SELECT … WITH LOCK?

  • En una subconsulta
  • En un JOIN
  • Con el operador DISTINCT
  • Con la cláusula GROUP BY
  • Con las funciones agregadas: COUNT(), SUM(), AVG(), MAX(), MIN(), LIST()
  • En una vista
  • En la salida de un stored procedure seleccionable
  • En una tabla externa

Conclusión:

En Firebird muy raramente se necesita que una transacción sea pesimista pero si ese es el caso hay varias maneras de conseguirlo, una de ellas es mediante el comando SELECT … WITH LOCK.

Si se usa este comando hay que tratar de que el conjunto resultado tenga muy pocas filas, idealmente una sola fila.

Si la condición de filtro de la cláusula WHERE cumple más de una fila, no se puede garantizar que el SELECT … WITH LOCK haya bloqueado a todas esas filas porque otra transacción concurrente podría haber realizado algún bloqueo.

Artículos relacionados:

Entendiendo a las transacciones

Evitando confilctos en las transacciones

Evitando actualizaciones concurrentes del mismo registro

Transacciones optimistas y transacciones pesimistas

Algo más sobre transacciones optimistas y transacciones pesimistas

¿Por qué en Firebird es preferible que las transacciones sean optimistas?

El índice del blog Firebird21

El foro del blog Firebird21

Ordenando una UNION

3 comentarios

Cuando creamos una UNION y queremos ver sus resultados ordenados … podemos encontrarnos con el problema de que no podemos conseguirlo. ¿Por qué? Porque el Firebird no permite que escribamos el nombre de una columna en una cláusula ORDER BY de un SELECT que se usará en una UNION.

Sin embargo, necesitamos ver el conjunto resultado ordenado. ¿Cómo lo conseguimos?

Ejemplo.

Tenemos una tabla llamada CLIENTES y otra tabla llamada PROSPECTOS. Los prospectos son los clientes potenciales, aquellas personas o empresas a las cuales aún no les hemos vendido pero ya nos hemos entrevistado con ellas y puede ser que les vendamos más adelante. O sea, son nuestros posibles futuros clientes.

UNION1

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

UNION2

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

En la Captura 1. vemos algunos datos de nuestros clientes, y en la Captura 2. vemos algunos datos de nuestros prospectos. Ahora quisiéramos unirlos para verlos a todos juntos, pero deseamos verlos ordenados alfabéticamente por nombre. Así que escribimos:

Listado 1.

SELECT
   CLI_NOMBRE,
   CLI_DIRECC,
   CLI_TELEFO,
   CLI_EMAILX
FROM
   CLIENTES
ORDER BY
   CLI_NOMBRE

UNION ALL

SELECT
   PRO_NOMBRE,
   PRO_DIRECC,
   PRO_TELEFO,
   PRO_EMAILX
FROM
   PROSPECTOS

Escribimos la cláusula ORDER BY en el primer SELECT y el Firebird nos responde con el mensaje: “Token unknown – line 11, column 4. UNION.

No funcionó, no aceptó que escribamos UNION después de escribir ORDER BY. Así que ahora probamos:

Listado 2.

SELECT
   CLI_NOMBRE,
   CLI_DIRECC,
   CLI_TELEFO,
   CLI_EMAILX
FROM
   CLIENTES

UNION ALL

SELECT
   PRO_NOMBRE,
   PRO_DIRECC,
   PRO_TELEFO,
   PRO_EMAILX
FROM
   PROSPECTOS
ORDER BY
   PRO_NOMBRE

Donde escribimos la cláusula ORDER BY en el segundo SELECT. Y la respuesta del Firebird es: “Invalid command. Invalid ORDER BY clause.

O sea que … ¡¡¡tampoco funcionó!!!

Sin embargo, deseamos ver al resultado de esa UNION ordenado por nombres. ¿Cómo lo conseguimos?

Tenemos dos técnicas:

  1. Usando un número de columna
  2. Usando una tabla derivada

Ordenando una UNION usando un número de columna:

Listado 3.

SELECT
   CLI_NOMBRE,
   CLI_DIRECC,
   CLI_TELEFO,
   CLI_EMAILX,
   'C' AS CLI_ORIGEN
FROM
   CLIENTES

UNION ALL

SELECT
   PRO_NOMBRE,
   PRO_DIRECC,
   PRO_TELEFO,
   PRO_EMAILX,
   'P' AS CLI_ORIGEN
FROM
   PROSPECTOS
ORDER BY
   1

Ordenando una UNION usando una tabla derivada:

Listado 4.

SELECT
   CLI_NOMBRE,
   CLI_DIRECC,
   CLI_TELEFO,
   CLI_EMAILX,
   CLI_ORIGEN
FROM (
      SELECT
         CLI_NOMBRE,
         CLI_DIRECC,
         CLI_TELEFO,
         CLI_EMAILX,
         'C' AS CLI_ORIGEN
      FROM
         CLIENTES
 
      UNION ALL

      SELECT
         PRO_NOMBRE,
         PRO_DIRECC,
         PRO_TELEFO,
         PRO_EMAILX,
         'P' AS CLI_ORIGEN
      FROM
         PROSPECTOS
     )
ORDER BY
   CLI_NOMBRE

¡¡¡Y ahora sí funcionó!!!

UNION3

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

Como podemos ver, a las 7 filas que teníamos en nuestra tabla de CLIENTES se le agregaron las 2 filas que teníamos en la tabla de PROSPECTOS y ahora nuestro conjunto resultado tiene 9 filas. Y además, vemos a todas esas filas ordenadas alfabéticamente por nombre. ¡¡¡Excelente!!!

Adicionalmente, habrás notado que tenemos una nueva columna, llamada CLI_ORIGEN, esa es una técnica muy usada cuando se realiza una UNION para poder distinguir el origen (o sea, de donde proviene) cada fila. En nuestro caso si hay una letra ‘C’ significa que el origen de esa fila es la tabla de CLIENTES, y si hay una letra ‘P’ entonces significa que el origen de esa fila es la tabla de PROSPECTOS. No siempre es necesario tener una columna que nos informe del origen de la fila, pero si alguna vez necesitas conocer ese dato, esta es la forma de hacerlo.

Explicación:

En el Listado 3. hemos usado un número que indica la posición de la columna que nos interesa (1=CLI_NOMBRE, 2=CLI_DIRECC, etc.). Podemos ordenar por cualquier columna, pero siempre recordando que debemos usar el número de esa columna, nunca su nombre. Y si queremos ordenar de mayor a menor entonces debemos usar la palabra DESCENDING, como en: “ORDER BY 1 DESCENDING”

En el Listado 4. creamos una tabla derivada que es la UNION entre la tabla CLIENTES y la tabla PROSPECTOS. El conjunto resultado que obtuvimos no está ordenado pero como ya sabemos el conjunto resultado de una subconsulta siempre es una tabla (y las tablas derivadas son una subconsulta que se usa después de la cláusula FROM). Entonces, y por lo tanto, podemos ordenar a esa tabla virtual.

Y así hemos solucionado nuestro problema.

Artículos relacionados:

El resultado de un SELECT es una tabla

Entendiendo subconsultas y tablas derivadas

Tablas derivadas

Optimizando un SELECT al usar una tabla derivada

Optimizando los JOIN

Optimizando las subconsultas

El índice del blog Firebird21

El foro del blog Firebird21

Ejemplo de recursión (6). Repitiendo las filas

3 comentarios

Un usuario del foro hizo esta pregunta: “Tengo una tabla llamada ETIQUETAS con dos columnas: NOMBRES Y NUMERO_REPETICIONES y deseo que cada nombre se repita esa cantidad de veces”.

La idea es imprimir etiquetas con esos nombres.

Solución:

Primero, creamos la tabla ETIQUETAS

ETIQUETAS1

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

Segundo, le insertamos algunas filas a la tabla de ETIQUETAS

ETIQUETAS2

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

Tercero, usando recursión obtenemos cada nombre, repetido la cantidad de veces indicada.

Listado 1.

WITH RECURSIVE MISETIQUETAS AS (
 
   SELECT
      ETI_NOMBRE,
      ETI_REPITE
   FROM
      ETIQUETAS
   WHERE
      ETI_REPITE > 0
 
   UNION ALL
 
   SELECT
      ETI_NOMBRE,
      ETI_REPITE - 1
   FROM
      MISETIQUETAS
   WHERE
      ETI_REPITE - 1 > 0
 
)

SELECT
   ETI_NOMBRE
FROM
   MISETIQUETAS

ETIQUETAS3

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

Y como podemos ver, cada nombre se ha repetido la cantidad de veces que habíamos escrito en la columna ETI_REPITE.

Explicación:

Primero, obtenemos todas las filas de la tabla ETIQUETAS cuya cantidad de etiquetas a imprimir es mayor que cero. Eso hace el primer SELECT, o sea nuestro “ancla” como habíamos visto en artículos anteriores.

Segundo, repetimos cada una de esas filas ETI_REPITE – 1 veces. ¿Por qué – 1? Porque la primera fila ya fue insertada por el “ancla” así que debemos insertar solamente las demás filas.

Tercero, mostramos cada nombre repetido la cantidad de veces que habíamos especificado en su columna ETI_REPITE. De eso se encarga el último SELECT.

Artículos relacionados:

Stored procedures recursivos

Entendiendo a las tablas autoreferenciadas

Usando CTE (Common Table Expression)

Otro ejemplo de CTE: ventas semanales

Usando varias CTE en una vista o en un stored procedure

FOR SELECT y tablas CTE

Usando recursividad con CTE

Ejemplo de recursión (1). Filas salteadas

Ejemplo de recursión (2). Numerar filas

Ejemplo de recursión (3). Fechas consecutivas

Ejemplo de recursión (4). Actualizando filas recursivamente

Ejemplo de recursión (5). Saldos acumulados

El índice del blog Firebird21

El foro del blog Firebird21

Older Entries Newer Entries