Archivo de Etiquetas de 'Access'

Formato no disponible en Snapshot de Access.

Hace un tiempo, cuando me dedicaba fundamentalmente a programar aplicaciones en Access, me encontré con un problema que irremediablemente todos mis clientes sufrían tarde o temprano. Es un problema que se refiere a las versiones XP y 2003 (ignoro si alguna más) y que ocurre cuando se quiere generar un informe en formato Snapshot. Este formato es muy útil para exportar informes en archivos .snp que se pueden visualizar desde prácticamente cualquier ordenador (y si no existe un visualizador gratuito de Microsoft).

Bien, el problema increíblemente reside en que al instalar Microsoft Access en español, la instalación crea una clave en el registro de Windows (regedit.exe) con la descripción en castellano:

snp,,1,Formato Snapshot (*.snp),0

Pero cuando el propio Access busca el valor para esa clave espera encontrarlo en inglés:

snp,,1,Snapshot Format (*.snp),0

Y al no encontrarlo muestra un mensaje diciendo que el formato en cuestión no está disponible. O sea, una chapuza monumental de los señores de Microsoft, sí.

Para solucionar esto simplemente hay que cambiar la entrada correspondiente en el registro de Windows para cambiar el primer valor por el segundo. Ojito, todos sabemos que puede ser crítico cambiar cosas en dicho registro, así que cada cual sabrá lo que hace (pero este cambio es bastante inofensivo, eso sí ;-) ). En cualquier caso la entrada a modificar es la siguiente para Access XP:

HKEY_LOCAL_MACHINE\_
     SOFTWARE\Microsoft\Office\10.0\Access\Report Formats

Y esta otra para Access 2003:

HKEY_LOCAL_MACHINE\_
     SOFTWARE\Microsoft\Office\11.0\Access\Report Formats

Y la entrada concreta a la que hay que cambiarle el valor es Snapshot Format.

A mis clientes solía enviarles un archivo .bat para facilitarles la modificación, que consistía simplemente en la instrucción:

REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
Office\11.0\Access\Report Formats" /v "Snapshot Format" /d "snp,
,1,Snapshot Format(*.snp),0" /f

Todo junto en la misma línea y sin espacios en los saltos de línea, ¿ok? Ah, y en donde pone 11.0 cambiarlo por 10.0 cuando la versión de Access sea la XP.

Por último añadir que en mi caso el uso del formato Snapshot era como paso intermedio para la creación de archivos PDF directamente desde Access sin necesidad de tener instalada ninguna impresora PDF, utilizando para ello la magnífica herramienta de Lebans.

Actualización: Iván aporta amablemente en los comentarios de esta misma entrada que en su caso para que pasara a funcionarle tuvo que cambiar el valor en castellano por este otro:

Formats\snp,,1,Snapshot Format (*.snp),0

Es decir, añadiéndole el Formats\ delante. Así que si estáis intentando resolver este problema y con las indicaciones del post no se os resuelve, no dejéis de probar también esta alternativa.

Simulando DLookup fuera de Access (II).

En el anterior post expuse una manera de realizar un DLookup en MySQL mediante stored procedures y stored functions y comenté que para el siguiente post me reservaba un modo de hacer lo mismo desde .NET. No obstante me permito intercalar otra manera de realizar otra vez el DLookup desde MySQL algo más sencilla que la primera aunque para recuperar el valor necesitaremos igualmente una doble consulta. Veamos cómo queda el código en esta ocasión:

#--------------------------------------------------------------------
# Author:      Albert Mata (www.albertmata.net)
# Date:        20080527
# Description: MySQL procedure to simulate Microsoft Access 
#              function DLookup. 
#--------------------------------------------------------------------

#--------------------------------------------------------------------
# Returns DLookup value using fourth parameter.
#--------------------------------------------------------------------
DROP PROCEDURE IF EXISTS DLookup;
DELIMITER //
CREATE PROCEDURE DLookup(IN campo VARCHAR(7),
                         IN tabla VARCHAR(14),
                         IN condicion VARCHAR(250),
                         OUT valor VARCHAR(250))
BEGIN

DROP TABLE IF EXISTS tbl_dlookupaux;
CREATE TABLE tbl_dlookupaux (tbl_res VARCHAR(250));
SET @query = CONCAT('INSERT INTO tbl_dlookupaux SELECT ',
                    campo, ' FROM ', tabla, ' WHERE ',
                    IF(ISNULL(condicion),'TRUE',condicion),
                    ' LIMIT 1');

PREPARE running FROM @query;
EXECUTE running;
DEALLOCATE PREPARE running;
SELECT tbl_res INTO valor FROM tbl_dlookupaux;
DROP TABLE IF EXISTS tbl_dlookupaux;

END
//
DELIMITER ;

Esta vez utilizo únicamente una stored procedure, pero lo hago tomando parámetros tanto de entrada (campo, tabla y condicion) como de salida (valor). Con esto lo que hago es almacenar el resultado en el parámetro valor en lugar de almacenarlo provisionalmente en una tabla para luego recuperarlo de allí (bueno, internamente la stored procedure sí almacena en una tabla temporal, pero en tanto que la misma stored procedure la borra este proceso es transparente al usuario).

Recuerdo que partíamos de una tabla cty_nicecities con esta estructura:

+--------+-----------+---------+
| cty_id | cty_nam   | cty_cou |
+--------+-----------+---------+
|      1 | Paris     | France  | 
|      2 | Rome      | Italy   | 
|      3 | Frankfurt | Germany | 
|      4 | Dortmund  | Germany | 
|      5 | Milan     | Italy   | 
+--------+-----------+---------+

La llamada debe ahora realizarse así:

CALL DLookup ('cty_nam', 'cty_nicecities', 'cty_id = 4', @my_city);
SELECT @my_city;

Almacenamos pues el valor en una variable (@my_city) y después lo rescatamos:

+----------+
| @my_city |
+----------+
| Dortmund |
+----------+

Tal vez este método es más limpio que el anterior. De todos modos para la próxima entrega, esta vez sí, postearé el DLookup mediante código en .NET, que será bastante más potente al incorporar gestión de errores y no tener limitaciones fruto de tener que especificar el tipo de datos concreto que esperamos obtener. No obstante un DLookup en MySQL se ejecutará íntegramente en el servidor, mientras que uno en .NET tendrá una parte ejecutándose en la máquina cliente, así que dependerá del gusto de cada cual escoger uno u otro. Yo me inclino por la versión en .NET…

Simulando DLookup fuera de Access (I).

Para todos aquellos que empezamos en bases de datos con Access y que programamos aplicaciones con VBA, las funciones DCount, DMax y similares nos resultan de lo más útiles para obtener valores puntuales de una base de datos sin tener que recuperar explícitamente un recordset. La más útil de todas probablemente sea DLookup. La sintaxis resumida de DLookup es la siguiente (para una más detallada explicación se puede consultar la propia ayuda de Access):

DLookup (nombre_campo, nombre_tabla, condicion_opcional)

Y nos devuelve el valor del campo correspondiente de la tabla indicada, en el primer registro que cumpla la condición opcionalmente pasada como parámetro.

Pues bien, como ya he comentado en posts anteriores, la base de datos con la que actualmente estoy trabajando es MySQL, y por desgracia esta función tan útil no existe de manera predeterminada en MySQL. No obstante, ello no quiere decir que no podamos utilizar algo similar, sí que podemos, aunque vamos a tener que programar un poco antes. He aquí el código:

#--------------------------------------------------------------------
# Author:      Albert Mata (www.albertmata.net)
# Date:        20080527
# Description: MySQL procedure and function to simulate Microsoft
#              Access function DLookup. 
#--------------------------------------------------------------------

#--------------------------------------------------------------------
# Creates an auxiliar table to store the value.
#--------------------------------------------------------------------
DROP PROCEDURE IF EXISTS PreDLookupAux;
DELIMITER //
CREATE PROCEDURE PreDLookup(campo VARCHAR(7), 
                            tabla VARCHAR(14), 
                            condicion VARCHAR(250))
BEGIN

DROP TABLE IF EXISTS tbl_dlookupaux;
CREATE TABLE tbl_dlookupaux (tbl_res VARCHAR(250));
SET @query = CONCAT('INSERT INTO tbl_dlookupaux SELECT ', 
                    campo, ' FROM ', tabla, ' WHERE ', 
                    IF(ISNULL(condicion),'TRUE',condicion), 
                    ' LIMIT 1');
PREPARE running FROM @query;
EXECUTE running;
DEALLOCATE PREPARE running;

END
//
DELIMITER ; 

#--------------------------------------------------------------------
# Gets the value from auxiliar table created before.
#--------------------------------------------------------------------
DROP FUNCTION IF EXISTS DLookup;
DELIMITER //
CREATE FUNCTION DLookup() RETURNS VARCHAR(250)
BEGIN

DECLARE aux VARCHAR(250); 
SET aux := (SELECT tbl_res FROM tbl_dlookupaux);
RETURN aux;

END
//
DELIMITER ;

Hay unas cuantas cosas que explicar:

1) Hasta la versión 5.0.45 de MySQL (la última GA cuando escribo esto y por tanto la de uso recomendado) las stored functions no permiten hacer ciertas cosas que harían mucho más fácil crear el DLookup en un solo paso, por eso se tiene que crear en un doble paso.

2) En el primer paso creamos una tabla auxiliar con un único campo y le insertamos un único registro con el valor que sale de la combinación campo-tabla-condicion que le hemos pasado. En el segundo paso nos devuelve ese valor que hemos almacenado temporalmente en la tabla auxiliar.

3) He puesto una longitud de 7 caracteres para el nombre del campo y de 14 para el de la tabla porque son las longitudes que tienen siempre mis nombres de campos y tablas, no obstante se puede modificar al gusto.

4) En cambio en tipo de campo de salida he puesto VARCHAR(250) por intentar ser algo genérico. Con esta definición nos devolverá sin problemas fechas y números, pero hay que tener en cuenta esta definición por si se utiliza DLookup con tipos de datos que no tengan cabida en un genérico VARCHAR(250).

5) Si no se desea pasar ningúna condición se puede establecer a cadena vacía ('') o bien a NULL, pero una stored function de MySQL no permite poner argumentos como de introducción opcional.

Veámoslo en funcionamiento. Tengo una tabla cty_nicecities con esta estructura:

+--------+-----------+---------+
| cty_id | cty_nam   | cty_cou |
+--------+-----------+---------+
|      1 | Paris     | France  | 
|      2 | Rome      | Italy   | 
|      3 | Frankfurt | Germany | 
|      4 | Dortmund  | Germany | 
|      5 | Milan     | Italy   | 
+--------+-----------+---------+

Y puedo utilizar mi DLookup particular de esta manera:

CALL PreDLookup ('cty_nam', 'cty_nicecities', 'cty_id = 4');
SELECT DLookUp();

Para obtener:

+-----------+
| DLookup() |
+-----------+
| Dortmund  | 
+-----------+

Y otro ejemplo, si pongo:

CALL PreDLookup ('cty_nam', 'cty_nicecities', 'cty_cou = \'Italy\'');
SELECT DLookUp();

Obtengo:

+-----------+
| DLookup() |
+-----------+
| Rome      | 
+-----------+

Funciona aceptablemente bien y nos puede resultar muy útil cuando queramos utilizar la función DLookup en MySQL, sin embargo no es una solución idonea. Quizá cuando MySQL elimine las restricciones comentadas anteriormente se pueda crear una mejor solución. Por lo pronto en el próximo post subiré una solución infinitamente más elegante y potente para hacer lo mismo sólo que en lugar de hacerlo desde el lado del servidor MySQL lo haremos desde el lado de una aplicación programada en .NET. ;-)

Actualización.

En el siguiente post hay una versión distinta más breve y elegante del DLookup en MySQL.




Creative Commons License
El blog de Albert Mata by Albert Mata is licensed under a Creative Commons Reconocimiento-Compartir bajo la misma licencia 2.5 España License.