diciembre 28, 2005

Nulos e Integridad Referencial

Trabajando en un proyecto reciente me encontré con este detalle que parece tener Firebird (al menos en las versiones 1.5.2 y hasta el 1.5.3 RC3 que es la que tengo en mi laptop). Resultó ser ese tipo de cosas que te encuentras y que te pueden tomar muchas horas en dilucidar en donde es que radica el problema y resulta ser algo risorio y que se le ha escapado a alguien en el desarrollo de Firebird.

Para empezar he de explicar el escenario donde me ocurrió esto, tengo una tabla llamada GESTORES donde a cada gestor se le puede o no asociar un usuario del sistema, entonces hay registros que tienen un valor en este campo y otros en los que simplemente se queda en nulo. Dado esto, en teoría esta consulta me debería dar todos los usuarios que no estan asociados a algún gestor:


Sin embargo, no fué así, esta consulta no me devolvía ni un solo valor aunque yo sabía que si habian por lo menos 15 usuarios que no estaban asociados a algún gestor.

De repente algun Dios olimpico iluminó mi mente y me llego la idea de que tal vez el hecho de que hubiera valores nulos en la tabla de gestores influiría, así que le agregué una pequeña condición para ignorar los valores nulos de la relación:


Con esto ya funcionó sin ningún problema el programa, ahora si me devolvía los 15 registros de los usuarios que no estaban asociados a gestores.

Pero la duda me aquejaba acerca de si esto era una constante en las relaciones WHERE NOT IN asi que me leí el magnífico artículo de Firebird Nulls Guide del sitio de Firebird, en el cual se explica mucho acerca del comportamiento de los valores nulos en las comparaciones y operaciones con Firebird, pero nada que me diera un Norte sobre porque mi consulta especificamente tenia un comportamiento tan errático. Probé con otras tablas de la misma base de datos y solo con estas dos me devolvia un conjunto vacío. Al rebuscar más meticulosamente cai en la cuenta de que la diferencia radicaba en que no había definido una regla de integridad referencial entre los campos de ambas relaciones, asi que raudo y veloz procedí a crearla:


Y una vez hecho esto ya la primer consulta funciona perfectamente. Osea que al final de cuentas el problema es que la combinación de comparación del tipo WHERE [NOT] IN SELECT falla cuando en el subselect existen valores nulos y no hay una relación de integridad entre ambas relaciones, esto pareciera trivial, pero cuando en un sistema tratas de explotar al máximo los datos que tienes las consultas de este tipo suelen ser muy comunes y si tienes un sistema de consultas dinámicas o algun experto programado que haga estas consultas puede caer muy fácilmente en estos casos y no ser validado.

Entonces como conclusión de todo esto podemos emitir la siguiente sentencia:

¡Cuidado con Firebird! porque la combinación de comparación del tipo WHERE [NOT] IN SELECT falla cuando en el subselect existen valores nulos y no hay una relación de integridad entre ambas relaciones.


Pueden hacer sus pruebas en sus propias configuraciones y ver que otras implicaciones tiene este detalle, esperemos que esto se arregle pronto.

Delphi del 1 al 10 Where version <> 8

¿Cual es su favorito?







diciembre 22, 2005

Escribir en el Canvas de un componente

Eneko alonso es un joven programador de Delphi que en su página web nos comparte esta muy útil funcion que permite escribir sobre el Canvas de algun componente en nuestras aplicaciones, pero les dejo que sean sus propias palabras quienes les expliquen el truco:
Para escribir texto transparente sobre el canvas de cualquier componente visual en Delphi, hay que usar la función SetBkMode de la API de Windows.

procedure TForm1.Button1Click(Sender: TObject);
var
AnteriorBkMode: integer;
begin
with Form1.Canvas do begin
Brush.Color := clRed;
TextOut(100, 80, 'Texto Opaco');
AnteriorBkMode := SetBkMode(Handle, TRANSPARENT);
TextOut(100, 100, 'Texto Transparente');
SetBkMode(Handle, AnteriorBkMode);
end;
end;

La directiva de proyectos

Por lo menos estamos en buenas manos

diciembre 14, 2005

Una cadena = Un número único

Esta función me pareció tres cosas: útil, interesante y sencilla

type
  UInt64 = 0..9223372036854775807;

  function Unc (s: string): UInt64;
  var
    x: integer;
  begin
    Result := 0;
    for x := 1 to Length (s) do
    begin
      Result := Result + ((Ord (s[x])) shl ((x - 1) * 8));
    end;
  end;

La lógica detrás de la función no es complicada, si bien si muy práctica y útil en varios casos cuando al manejar elementos en memoria interesa obtener identificadores únicos por alguna serie de textos ( me he encontrado con casos así al trabajar con sistemas de redes o árboles). Un inconveniente es que los números que se obtienen por esta función son muchas veces números muy grandes (de hasta 64 bits) por lo que la aplicación en una BD como Firebird por ejemplo resulta un tanto complicada; pero bueno no es tan fácil tenerlo todo siempre.

diciembre 04, 2005

Todo mundo tiene su estilo

Es muy sabido que en esta vida todo mundo tiene su propio estilo para programar, cada quien se entiende con sus propios garabatos. Hay a quienes les gusta hacer miles y miles de funciones(y luego de tantas que tienen ni se acuerdan que las tienen cuando necesitan una en particular), otros que son muy metódicos en cuanto al número de lineas que ha de contener un proceso, otros que cuidan que en tal o cual paso la pila del procesador no tenga mas de X retornos acumulados de procedimientos o funciones, O que les gusta ahorrar hasta el mas ínfimo bit de memoria y otros a quienes les vale un soberano grano de café molido ese asunto y prefieren sacrificar mucha memoria para darle mayor enfasis a alguna otra funcionalidad de la aplicación.

Mas de una vez me ha tocado descifrar marañas entretejidas de cosas que parecen no tener mucho sentido pero como diría Galileo
"Sin embargo funcionan..."


Mas tambien sin embargo es un verdadero martirio darle seguimiento y mantenimiento a aplicaciones escritas así, la mayoría de las veces que eso me sucede y mientras pueda hacerlo prefiero reescribir el módulo o aplicación a mi manera

Y es que realmente uno se acostumbra a trabajar con su propio estilo, a identar de tal o cual manera su código, a seguir ciertas normas, a ignorar algunas otras, etc...Y no solo en cuanto a Código en Object Pascal, sino tambien en el caso de las consultas en SQL, las politicas de mantenimiento y desarrollo sobre la estructura y código en la Base de Datos, en fin.

Pero a todo esto, existen ciertos estandares que fincan por ejemplo las empresas de desarrollo para que el trabajo de su personal sea lo mas colaborativo y transparente posible. Por eso llamó mi atención la publicación que encontré de los estandares que usan en Indy con los que comparto mucha afinidad y de los que debería tomar algunas notas en cuenta y agregarlas en mi propio estilo. Otros muy buenos estandares son los propuestos por Marco Cantu en sus 20 reglas para la OOP en Delphi, las cuales se publicaron en la Delphi Magazine, que andan por ahi volando en algunos sitios, pero desconozco si esta permitida su distribución abierta