Fechas aleatorias

Deja un comentario

A veces cuando estamos realizando pruebas necesitamos datos aleatorios, o sea datos que pueden ser cualesquiera, no podemos anticipar cuales serán.

Supongamos que necesitamos fechas aleatorias. Para ello, como de costumbre usaremos la función RAND(). Esta función nos devolverá un número entre 0 y 0,999999. También usaremos la función FLOOR() que nos devuelve la parte entera de un número. También usaremos la función DATEADD() que le suma un número a una fecha para devolvernos una nueva fecha, la fecha original más la cantidad de días que le hemos sumado.

Veamos algunos ejemplos:

Listado 1. Fechas entre el 1 de enero de 2017 y el 31 de marzo de 2017

SELECT
   DATEADD(FLOOR(90 * RAND()) DAY TO DATE '2017-JAN-01')
FROM
   RDB$DATABASE

Explicación:

La cantidad de días posibles es 90, así que multiplicamos a RAND() por 90. La cantidad de días debe ser un número entero, así que usamos la función FLOOR(), porque la función RAND() nos devuelve un número con decimales. Lo que estamos sumando son días así que usamos DAY, la fecha la expresamos como un string así que debemos convertirla a tipo fecha con DATE.

En síntesis, lo que estamos diciendo es: muéstrame una fecha aleatoria entre el 1 de enero de 2017 y el 31 de marzo de 2017

Listado 2. Fechas entre el 1 de enero de 2017 y el 31 de diciembre de 2017

SELECT
   DATEADD(FLOOR(365 * RAND()) DAY TO DATE '2017-JAN-01')
FROM
   RDB$DATABASE

Explicación:

Como la cantidad de días posibles es 365, entonces multiplicamos a 365 por RAND()

Listado 3. Fechas entre el 1 de abril de 2017 y el 30 de abril de 2017

SELECT
   DATEADD(FLOOR(30 * RAND()) DAY TO DATE '2017-APR-01')
FROM
   RDB$DATABASE

Explicación:

Como la cantidad de días posibles es 30, entonces multiplicamos 30 por RAND(). Como la fecha inicial es el 1 de abril de 2017, entonces escribimos esa fecha después de DATE.

Artículos relacionados:

La función DATEADD()

La función FLOOR()

La función RAND()

El índice del blog Firebird21

El foro del blog Firebird21

Agrupando por una columna … y viendo todas las demás columnas

Deja un comentario

Como seguramente ya sabes, puedes usar la cláusula GROUP BY para agrupar filas de una tabla. Todo bien hasta ahí pero pronto descubrirás que a veces te encuentras con un problema … en tu SELECT solamente puedes ver las columnas que has agrupado.

Ejemplo: Tenemos una tabla llamada VENTASCAB (cabecera de ventas) que tiene los siguientes datos:

AGRUPAR1

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

Queremos ver sus columnas VTC_NRODOC, VTC_FECHAX, VTC_IDECLI y VTC_TOTALX pero solamente cuando la fecha está repetida. En otras palabras, si en una fecha se hicieron dos o más ventas queremos ver esas filas; si se hizo una sola venta, no queremos ver.

Listado 1. (no funcionará)

SELECT
   VTC_NRODOC,
   VTC_FECHAX,
   VTC_IDECLI,
   VTC_TOTALX
FROM
   VENTASCAB
GROUP BY
   VTC_NRODOC,
   VTC_FECHAX,
   VTC_IDECLI,
   VTC_TOTALX
HAVING
   COUNT(*) >= 2

Si ejecutamos el Listado 1. no obtendremos ni siquiera una fila. ¿Por qué no? porque estamos agrupando por VTC_NRODOC y el contenido de esa columna nunca se repite.

Listado 2. (no funcionará)

SELECT
   VTC_FECHAX
FROM
   VENTASCAB
GROUP BY
   VTC_FECHAX
HAVING
   COUNT(*) >= 2

Si ejecutamos el Listado 2. obtendremos la Captura 2. pero … no es lo que estamos necesitando.

AGRUPAR2

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

En la Captura 2. hemos agrupado por fechas que se repiten 2 ó más veces pero no podemos ver las columnas VTC_NRODOC, VTC_IDECLI, y VTC_TOTALX, y queremos verlas, entonces ¿cómo lo conseguimos?

Usando una gran construcción del Firebird que es la subconsulta filtrada. O sea, una subconsulta que se utiliza en la cláusula WHERE.

Listado 3. (Sí funcionará)

SELECT
   VTC_NRODOC,
   VTC_FECHAX,
   VTC_IDECLI,
   VTC_TOTALX
FROM
   VENTASCAB
WHERE
   VTC_FECHAX IN (SELECT
                     VTC_FECHAX
                  FROM
                     VENTASCAB
                  GROUP BY
                     VTC_FECHAX
                  HAVING
                     COUNT(*) >= 2)

Si ejecutamos el Listado 3. obtendremos la Captura 3. que nos mostrará exactamente lo que estábamos necesitando.

AGRUPAR3

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

O sea que si la fecha está repetida, queremos ver los datos de cada una de las ventas realizadas en esa fecha.

Si te fijas, notarás que la subconsulta del Listado 3. es igual a la consulta del Listado 2. ¿Qué estamos pidiendo en el Listado 3.? Que nos muestre las filas de la tabla VENTASCAB cuyas fechas se obtengan de la subconsulta. O sea, las fechas mostradas en la Captura 2. Por lo tanto, solamente si una fecha se encuentra en la Captura 2. esa fila será mostrada.

Conclusión:

Una subconsulta filtrada puede ser extremadamente útil en muchas circunstancias y debemos aprender a utilizarla. Lo que hacemos con ella es filtrar a una tabla o una vista para que solamente se utilicen las filas que cumplen con la condición que le imponemos.

Como en nuestro ejemplo la fecha debe estar repetida entonces debemos filtrar usando la columna de fecha (VTC_FECHAX se llama en esta tabla). Desde luego que podemos filtrar por cualquier otra columna, siempre y cuando tenga valores repetidos esta técnica funcionará.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

 

 

Velocidad de acceso a constantes, variables y columnas

Deja un comentario

A veces tenemos la alternativa de usar en un SELECT una constante, una variable, o el valor de una columna. Desde el punto de vista de la velocidad ¿hay diferencias entre cuál de ellas usar?

Alternativa 1. Usando una constante.

SELECT
   MiColumna
FROM
   MiTabla
WHERE
   MiColumna = 4567

Alternativa 2. Usando una variable.

SELECT
   MiColumna
FROM
   MiTabla
WHERE
   MiColumna = RDB$GET_CONTEXT('USER_SESSION', 'MIVALOR')

Alternativa 3. Usando el valor de una columna.

SELECT
   MiColumna
FROM
   MiTabla
WHERE
   MiColumna = (SELECT MiColumnaInteresante FROM MiOtraTabla)

Y en este caso MiOtraTabla tiene una sola fila.

¿Cuál de estas tres alternativas es la más rápida?

La más rápida es la Alternativa 1., luego le sigue la Alternativa 2. y finalmente la Alternativa 3, la más lenta de todas.

Claro que las diferencias de velocidad se miden en milisegundos así que en la práctica muchas veces será lo mismo si utilizas cualquiera de esas alternativas.

Artículos relacionados:

Usando variables en Firebird

El índice del blog Firebird21

 

Usando UNION para reemplazar condiciones complejas

Deja un comentario

En ocasiones tenemos que escribir un SELECT que tiene una cláusula WHERE muy compleja, está llena de condiciones, de AND, de OR, y entender lo que hace nos resulta bastante complicado. Una forma de facilitar la lectura es usando la cláusula UNION.

Usando UNION es similar a usar OR pero:

  1. Se escribe más al usar UNION que al usar OR
  2. Se entiende mejor al usar UNION que al usar OR cuando las condiciones son complejas

Ejemplo:

Tenemos una tabla llamada CLIENTES que tiene estas filas:

UNION1

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

Y queremos una consulta que nos muestre los clientes cuyos nombres empiecen con la letra “M” o con la letra “S”. Podríamos escribir una consulta como esta:

SELECT
   CLI_NOMBRE
FROM
   CLIENTES
WHERE
   CLI_NOMBRE STARTING WITH 'M' OR
   CLI_NOMBRE STARTING WITH 'S'

Y está muy bien, como la consulta es simple esa es la solución adecuada. Pero si queremos podríamos obtener el mismo resultado usando la cláusula UNION.

SELECT
   CLI_NOMBRE
FROM
   CLIENTES
WHERE
   CLI_NOMBRE STARTING WITH 'M'

UNION

SELECT
   CLI_NOMBRE
FROM
   CLIENTES
WHERE
   CLI_NOMBRE STARTING WITH 'S'

Entonces, la primera forma es la adecuada cuando las condiciones son simples. Usamos OR y asunto solucionado. Pero cuando las condiciones son complicadas es preferible usar UNION porque nos facilitará la lectura y nos resultará más fácil comprender que hace nuestro SELECT.

Conclusión:

Se puede usar la cláusula UNION para reemplazar al operador condicional OR pero solamente se justifica usar UNION cuando las condiciones son complicadas de entender, ya que al usar UNION es fácil entender lo que está ocurriendo.

Podemos pensar en UNION como en una suma. Nos devuelve el resultado de la primera consulta MÁS el resultado de la segunda consulta.

En nuestro ejemplo, los nombres de los clientes que empiezan con “M” más los nombres de los clientes que empiezan con “S”.

Artículos relacionados:

Entendiendo a las UNION

El índice del blog Firebird21

Eligiendo entre LIKE y CONTAINING en los SELECT

5 comentarios

Cuando en una consulta la condición de búsqueda es alfanumérica podemos usar (entre otras posibilidades)  a LIKE y a CONTAINING.

¿Cuál es preferible?

LIKE distingue entre mayúsculas y minúsculas, en cambio CONTAINING no las distingue.

Ejemplo:

Tenemos una tabla llamada BANCOS con estas filas:

LIKE1

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

SELECT
   *
FROM
   BANCOS
WHERE
   BAN_CODSUC >= 0 AND
   BAN_NOMBRE LIKE '%BAN%'

LIKE2

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

En la Captura 2 podemos ver cuales son las filas que obtuvimos al usar LIKE.

SELECT
   *
FROM
   BANCOS
WHERE
   BAN_CODSUC >= 0 AND
   BAN_NOMBRE CONTAINING 'BAN'

LIKE3

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

Y en la Captura 3 las filas que obtenemos al usar CONTAINING.

Como puedes ver, con CONTAINING obtuvimos más filas porque nos muestra las que están en mayúsculas y también las que están en minúsculas. Por eso en general, cuando los nombres no están estandarizados es preferible usar CONTAINING.

Esto sucede a menudo cuando son varias las personas encargadas de registrar los datos en una tabla. Algunos escriben todo en mayúsculas y algunos mezclan mayúsculas con minúsculas. Entonces, para asegurarnos que encontremos todos los datos buscados, sea que los hayan escrito en mayúsculas o en minúsculas debemos usar CONTAINING.

Artículo relacionado:

El índice del blog Firebird21

Algunos ejemplos de funciones agregadas

Deja un comentario

En otros artículos ya habíamos visto las funciones agregadas, aquí hay algunos ejemplos más de como usarlas.

SELECT
   SUM(IIF(MOV_CODSUC = 0, MOV_CANTID * MOV_PRECIO, 0)) AS TOTAL0,
   SUM(IIF(MOV_CODSUC = 1, MOV_CANTID * MOV_PRECIO, 0)) AS TOTAL1
FROM
   MOVIMDET

En esta consulta, si el Código de la Sucursal es 0 se realiza la multiplicación y se halla el Total de esa Sucursal. Lo mismo con la Sucursal 1.

SELECT
   COUNT(DISTINCT IIF(MOV_CODSUC = 0, MOV_CANTID, NULL)) AS TOTAL0,
   COUNT(DISTINCT IIF(MOV_CODSUC = 1, MOV_CANTID, NULL)) AS TOTAL1
FROM
   MOVIMDET

En esta consulta contamos cuantas cantidades distintas hay en la Sucursal 0 y en la Sucursal 1 de la tabla MOVIMDET.

SELECT
   COUNT(DISTINCT IIF(MOV_CODSUC = 0, MOV_IDEPRD, NULL)) AS TOTAL0,
   COUNT(DISTINCT IIF(MOV_CODSUC = 1, MOV_IDEPRD, NULL)) AS TOTAL1
FROM
   MOVIMDET

Y en esta consulta contamos cuantos productos distintos hay en la tabla MOVIMDET (la columna MOV_IDEPRD es el Identificador del Producto).

Conclusión:

Lo interesante de los ejemplos es que muestran que dentro de las funciones agregadas se pueden escribir otras funciones.

Artículo relacionado:

El índice del blog Firebird21

 

Consultas con columnas condicionales

2 comentarios

A veces en nuestras consultas necesitamos mostrar columnas cuyos contenidos provienen de otras tablas y además lo hacen en forma condicional.

Ejemplo:

En una tabla MOVIMCAB tenemos una columna llamada MVC_TIPMOV la cual nos indica el tipo de movimiento (‘ECM’ es una entrada por compras, ‘SVT’ es una salida por ventas, etc.) y donde también guardamos el Identificador del Proveedor (cuando se trata de una compra) o el Identificador del Cliente (cuando se trata de una venta).

Ahora, necesitamos una consulta que nos muestre el nombre del Proveedor o el nombre del Cliente, según corresponda. Para ello haremos uso de la construcción CASE … WHEN … END

SELECT
   MVC_FECHAX,
   MVC_NRODOC,
   MVC_TIPMOV,
   CASE
      WHEN MVC_TIPMOV = 'ECM' THEN (SELECT PRO_NOMBRE FROM PROVEEDORES WHERE PRO_IDENTI = MVC_IDEPRO)
      WHEN MVC_TIPMOV = 'SVT' THEN (SELECT CLI_NOMBRE FROM CLIENTES    WHERE CLI_IDENTI = MVC_IDECLI)
   END AS NOMBRE
FROM
   MOVIMCAB
WHERE
   MVC_TIPMOV IN ('ECM', 'SVT')

Y este es el resultado (parcial, por supuesto) que obtuvimos:

CONDICIONAL1

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

Como puedes ver, en la columna NOMBRE tenemos el nombre de un Proveedor (cuando se trata de una compra) o el nombre de un Cliente (cuando se trata de una venta). Y para saber de cual de ellos se trata podemos mirar el contenido de la columna MVC_TIPMOV.

ERROR: Multiple rows in singleton select

Si ves ese mensaje de error significa que el SELECT interno devolvió más de una fila pero debería haber devuelto solamente una fila (o ninguna, pero jamás más de una).

¿Cuál es el problema?

Que si el SELECT devuelve varias filas el Firebird no puede saber cual de esas filas es la correcta. Por ejemplo, si el SELECT devuelve el nombre de 500 clientes ¿cuál de esos 500 nombres es el que debe mostrar? imposible que el Firebird pueda saberlo y por lo tanto te muestra un mensaje de error.

Para evitar el error siempre tienes que asegurarte que la condición que pusiste en la cláusula WHERE limite el resultado a una sola fila.

En el ejemplo de arriba se usó el Identificador del Proveedor o del Cliente. Como esos identificadores son únicos y jamás pueden repetirse entonces estamos seguros de que jamás obtendremos el error “multiple rows in singleton select”. Sin embargo cuando no usas identificadores ni claves primarias ni claves únicas la posibilidad de encontrar ese error siempre existirá.

Artículo relacionado:

El índice del blog Firebird21

Older Entries