Usar índices con valores nulos

En Oracle, los valores nulos no se indexan. Esto hace que las consultas que usan “is null” o “is not null” como condición requieran un table full scan.

Para evitar eso una forma es poner un valor especial para representar el null. Por ejemplo cero.

Así se va a usar ese índice sin problema. El tema es que hay que asignar un valor para que represente el null.

Oracle no crea índices para las claves foráneas

Cuando se define una restricción de clave foránea en Oracle, el motor no crea un índice para la columna que se restringe. Eso significa que cualquier consulta sobre la tabla del estilo

select *
from detalle
where id_maestro = 10

no serán optimizadas. Es un regla en el 99% de los casos que ese índice será necesario.

En el ejemplo la clave foránea sería la que hace que la columna id_maestro de la tabla detalle es la clave primaria de otra tabla.

Optimización al leer de un ResultSet

Acabo de desasnarme respecto a una funcionalidad que está disponible en JDBC y que me parece muy prometedora.

Cuando se leen datos de la base se hace de a una cierta cantidad de filas por lectura. Si sabemos de antemano que vamos a leer todo el result set y tenemos memoria disponible, podemos aumentar esa cierta cantidad del valor por omisión (10) a algo más grande.

Esto reduce la cantidad de lecturas y hace que cada lectura sea más extensa.

Es muy sencillo, al crear el PreparedStatement le seteamos el fetch size:

procedure.setFetchSize(5000);

Generar letras al azar en C

Una pequeña demostración de cómo generar letras al azar en C.

#include<stdlib.h>
#include<time.h>
#include<stdio.h>
int main(int c, char** s){
    int i;
    srand(time(NULL));
    for(i = 0; i < 100; i++){
        printf("La letra es: '%c'n", 'a' + rand() % (('z' - 'a') + 1));
    }
    return 0;
}

Malabarismo en Oracle

Tengo que copiar unos 30 millones de registros de una tabla en la que hay unos 200 millones a otra tabla y obtuve un error: no se puede agrandar el índice porque no hay espacio en el tablespace. La base es Oracle 9.2

Para solucionarlo tuve que mover todas las tablas e índices de ese tablespace a otros tablespaces que tuvieran lugar. Así liberé 3 GB.

Luego saqué de la tabla destino las columnas que no necesitaba así sólo traeré las realmente indispensables.

Espero que me alcance el lugar porque si no tendré que llamar al DBA.

Así se puede ver el espacio libre en cada tablespace

select tablespace_name, trunc(sum(bytes) / (1024 * 1024)) as “MB Libres”
from user_free_space
group by tablespace_name

Para mover todas las tablas de un tablespace a otro hice un script SQL que me generó las sentencias.

select ‘ALTER TABLE ‘|| table_name ||’ MOVE TABLESPACE TABLESPACE_LIBRE;’ as query
from user_tables
where tablespace_name = ‘TABLESPACE_LLENO’

Y lo mismo para los índices.

select ‘ALTER INDEX ‘|| index_name ||’ REBUILD TABLESPACE TABLESPACE_LIBRE;’ as query
from user_indexes
where tablespace_name = ‘TABLESPACE_LLENO’

Leer de teclado con scanf

Hay constumbres tremendamente nocivas y muy difíciles de revertir. En cualquier apunte de clase de C los profesores dan ejemplos simples en los que usan scanf para leer enteros de teclado. Eso no sólo es absolutamente innecesario (podrían ser parámetros de la función en lugar de leerlos) sino que es nocivo.

1) Scanf está pensada para leer entrada con un determinado formato y el formato de lo que se ingresa con el teclado depende del ser humano que está tipeando.

2) Scanf termina de leer cuando termina el formato y no cuando lee todo el buffer de teclado. Esos caractres que quedaron en el buffer serán leídos en una futura invocación de cualquier función que lea de teclado, casi seguramente en forma inesperada.

3) Scanf lee cualquier cosa cuando espera un número decimal y se ingresa una letra o un caracter que no esté entre 0 y 9. Y no sabemos si realmente el usuario ingresó ese -244234245235 o si es que ingresó “hola” y scanf lo interpretó así.

4) A veces los programamdores se ven tentados de “arreglar” los problemas que tiene scanf para leer de teclado, generando, por supuesto, más problemas en otras partes de la aplicación.

Para leer de teclado, lo correcto y lo ideal, es leer caracter por caracter, aunque cueste un poquitín más al principio. A la larga, las ventajas son enormes en que no habrá sorpresas en lo que obtenemos al leer.

Decimal vs. Binario

Día a día veo cómo los informáticos seguimos tropezándonos con la misma piedra. Usar números de coma flotante para representar montos de dinero es incorrecto.

Veamos por qué.

1) Los números de coma flotante (de 16, 32, 64 u 80 bits) no pueden representar todos los valores dentro de su rango. Cuando se topan con un valor de esos, lo reemplazan por el más cercano. Es ahí cuando vemos números como 1.000000000000001 ó 3,66666666666667 cuando en realidad queremos 1 y 3,666666666666666.

2) Ese mínimo error de representación se va acumulando a medida que hacemos operaciones como sumas, multiplicaciones, etc. y terminan cambiando un monto de $10 por $10,01 o $9,99.

3) Ese centavito perdido no se hubiera perdido si hubiéramos hecho la cuenta como nos enseñaron en la escuela. Es un error que puede ser subsanado.

4) El problema es que para dinero una diferencia de $0,000000000001 es insignificante, pero no debemos permitir que esas diferencias se vayan acumulando y terminen generando diferencias de $1 o mayores.

La solución existe desde que se hizo el primer sistema que calculaba sumas de dinero, probablemente en COBOL, pero asombrosamente aún hoy sigue habiendo profesionales que definen montos de dinero como float o double.

La aritmética decimal precisa es la respuesta. En lugar de representar los números en binario de coma flotante, los representamos en decimal y en lugar de usar la unidad de coma flotante (muy veloz) presente en los procesadores desde el Intel 386, hacemos las cuentas con rutinas comunes, en general en lenguaje de alto nivel.

La ventaja, obviamente, es que tenemos el control sobre cómo queremos redondear en cada operación. Así cuando calculamos 1/3, podemos decidir si queremos 1, 2, 3 o 200 lugares después de la coma, pero jamás tendremos 0,3333333334, sino 0,333333333 o 0,33333333333333333333333333333. La longitud es arbitraria. Por ejemplo en Java, un BigDecimal puede tener como 2.000 millones de lugares después de la coma. En una base de datos como Oracle, tenemos un límite menor, pero generoso: algo así como 24 lugares.

En conclusión, para montos de dinero, jamás defina sus variables ni como float, ni double, ni single, ni extended. Use Numeric, Decimal, Digits, BigDecimal, etc. según su lenguaje o base de datos le provea.

Y tenga cuidado con MySQL si no usa la versión 5.1 o superior y el modo estricto, ya que por más que defina un Decimal, las cuentas las estará haciendo en coma flotante.