Hace unas semanas me tocó revisar unos 300 ficheros sql para tratar de sacar información sobre una base de datos que íbamos a migrar. Sin documentación, con tablas y campos en desuso, sin integridad referencial... un trabajo duro. Así que la mejor opción que teníamos para sacar el esquema de datos era analizar consultas que sabíamos que se ejecutaban sobre ella, y funcionaban correctamente.
Estaba centrándome en una tabla concreta, y quería obtener sus relaciones, por lo que pretendía filtrar los ficheros para quedarme con los que hiciesen referencia a ella. Abrí mi explorador de archivos de XP, me fui a la carpeta en la que estaban todos los ficheros, y busqué metiendo el nombre de la tabla en el campo 'Una palabra o frase en el archivo'.
Cero resultados.
Tras un par de comprobaciones (ni había escrito mal el nombre de la tabla, ni estaba distinguiendo mayúsculas...), empecé a olerme que algo iba mal. Abrí algunos ficheros, y pude ver que en varios aparecía el nombre. Entonces, ¿por qué la búsqueda no arrojaba resultados?
Pues porque los ficheros tenían extensión .sql. Si cambiaba alguno a .txt, automáticamente aparecía en los resultados.
Todavía no he podido averiguar a qué se debe esto. Si es algún problema de configuración, o simplemente a Windows no le caen bien estos ficheros. El caso es que he comprobado todas las opciones de la búsqueda, y no veo nada raro. En la opción de Tipos de Archivo tengo marcado Todos los archivos y carpetas, y no se me ocurre mucho más.
Por tanto, la única solución que he podido aplicar es la de utilizar un programa para cambiar la extensión de todos los ficheros de la carpeta. A partir de ahí, pude seguir con mi infernal base de datos.
Mostrando entradas con la etiqueta SQL. Mostrar todas las entradas
Mostrando entradas con la etiqueta SQL. Mostrar todas las entradas
miércoles, 18 de abril de 2012
martes, 11 de octubre de 2011
Uso de parámetros en Jasper
Cuando hacemos un informe con Jasper Reports, es muy normal que utilicemos parámetros. De esta forma podemos enviar datos al informe que serán utilizados a la hora de generarlo. Los casos más habituales quizá sean el mostrar directamente el valor del parámetro, o el usarlo a su vez como parámetro para la consulta SQL que ejecutamos dentro de report. En este caso sólo tenemos que definir el parámetro en la sección correspondiente de iReport, y hacer referencia a él con el formato $P{nombre_parametro}, como se muestra en la imagen.
Pero podemos ir más allá. Por ejemplo podríamos recibir como parámetro la sentencia SQL a ejecutar, o alguna de sus cláusulas. En este caso buscaríamos hacer algo como esto.
Pero si tratamos de ejecutar un informe con este formato no funcionará, y nos mostrará una excepción:
net.sf.jasperreports.engine.JRException: Error executing SQL statement for : report1
Para entender por qué pasa esto, tenemos que entrar en detalle y ver qué hace Jasper cuando procesa los parámetros.
Cuando en la definición de la consulta de un Jasper utilizamos un parámetro con el formato $P{nombre_parametro}, lo que la herramienta genera es realmente una consulta SQL con parámetros. Es decir, tendríamos una consulta tal que select * from tabla where campo = ?.
Ahora bien, si lo que vamos a recibir como parámetro es una sentencia que queremos inyectar directamente en la consulta SQL, esta forma de trabajo no funcionaría. En este caso tenemos que cambiar el formato con el que hacemos referencia al parámetro por $P!{nombre_parametro}. De esta forma le indicamos a la herramienta que no queremos pasar un parámetro a la consulta SQL, sino que queremos directamente incluir el valor textual del parámetro como parte de la consulta.
lunes, 23 de mayo de 2011
Bajo rendimiento de consulta Oracle con upper
Hace poco estaba trabajando, y tenía una consulta sobre mi base de datos Oracle, que obtenía una sería de datos haciendo un filtrado por un campo de tipo varchar2. La consulta parecía funcionar bien, y tenía un rendimiento perfectamente aceptable. Algo como:
select * from tabla where campo=:parametro
Pero al poco tiempo me di cuenta de que para que la consulta funcionase correctamente en todos los casos, necesitaba que no se distinguiese entre mayúsculas y minúsculas al hacer la comparación. Por tanto, añadí una llamada a upper para el campo (el parámetro siempre llega en mayúsculas).
select * from tabla where upper(campo)=:parametro
De esta forma la consulta funcionaba como yo necesitaba. El problema es que, para mi sorpresa, el rendimiento cayó en picado.
En principio busqué la explicación en la llamada a upper en sí, pero lógicamente este tratamiento no es tan pesado como para producir tanto retraso. El problema está en que el buen tiempo de respuesta de la consulta original se producía gracias a la existencia de un índice sobre la columna campo. Al pasar a hacer la comparación por upper(campo), Oracle no puede utilizar el índice, y el tiempo de respuesta crece exponencialmente.
Ante esta situación, una solución es crear un índice directamente sobre la expresión upper(campo), en lugar (o además) de directamente sobre campo.
create index campo_index on tabla (upper(campo))
Eso sí, hay que tener en cuenta que para que el índice sea utilizado en este caso, es necesario modificar ligeramente la consulta, y asegurarse de que no se obtienen valores nulos.
lunes, 20 de septiembre de 2010
rownum mayor que...
Cuando trabajamos con consultas en Oracle, disponemos de la pseudo-columna rownum. Se le llama pseudo-columna porque aunque no sea una columna de nuestra tabla, Oracle la añade al resultado de la consulta, poniéndola a nuestra disposición, si la queremos.
El valor obtenido es un número que indica el orden en el que Oracle selecciona la fila de la tabla. Generalmente esta pseudo-columna se utiliza en consultas con una cláusula de ordenación, para obtener los N primeros registros según el criterio de ordenación indicado. Por ejemplo:
select * from (select * from empleados order by salario) where rownum < 10
Es lo que en otros SGBD se consigue con TOP.
Y en este punto es donde uno se ve tentado de ir más allá, y utilizar el rownum para paginar nuestras consultas. Es decir, para obtener, por ejemplo, del décimo al vigésimo empleado con mayor salario.
select * from (select * from empleados order by salario) where rownum <20 and rownum > 10
Sin embargo, nos llevamos la sorpresa de que esta consulta no devuelve datos. Explicar por qué es ligeramente complicado, pero lo intento.
Cuando ejecutas esa consulta Oracle obtiene la ristra de filas, y asigna a la primera el valor rownum=1. Entonces, evalúa la condición para esa fila, y como 1 no está entre 10 y 20, la elimina del resultado. Entonces, vuelve a comenzar el proceso, asignando de nuevo el valor rownum=1, a la que realmente era la segunda fila obtenida. Lógicamente, la condición vuelve a no cumplirse, la segunda fila es eliminada, y el valor rownum=1 es asignado a la tercera fila. Así hasta que nos hemos quedado sin nada.
¿Significa eso que no podemos utilizar una condición de tipo rownum mayor o igual que... en una consulta? Pues no, existe una forma de realizar esta comprobación. Para ello necesitamos que la pseudo-columna evolucione y se convierta en una columna con todas las de la ley. Si realizamos la siguiente consulta
select rownum rnum, empleados.* from (select * from empleados order by salario) empleados
obtenemos la tabla empleados ordenada por salario, con una columna (ya no pseudo-columna) rnum añadida, que asigna un número secuencial a cada una de las filas ordenadas. Por tanto, si sobre esta consulta obtenemos sólo los registros con rnum entre 10 y 20, tenemos lo que queríamos.
select * from (
select rownum rnum, empleados.* from (select * from empleados order by salario) empleados
) where rnum < 20 and rnum > 10
Es importante fijarse en que la consulta original, en la que se obtienen todas las filas de la tabla ordenadas, debe ser siempre la primera en ejecutarse, sin añadir nada. Si por ejemplo intentásemos hacer:
select * from (
select rownum rnum, empleados.* from empleados order by salario empleados
) where rnum < 20 and rnum > 10
Estaríamos obteniendo rnum y ordenando en la misma sentencia. Lo que pasaría entonces es que la ordenación se haría después de asignar los rnum, por lo que estaríamos asignando los secuenciales a las filas sin seguir el criterio de ordenación. La consulta devolvería registros, pero no los que nos interesan.
Para los políglotas, pongo un enlace bibliográfico y todo:
lunes, 13 de septiembre de 2010
Conectar a SQL Server desde SQLDeveloper
SQLDeveloper es una herramienta bastante apañada que ofrece Oracle para realizar los trabajos más habituales de los desarrolladores sobre bases de datos: ejecución de scripts, generación de DDL y DML, navegación sobre los objetos de los esquemas, depuración de bloques PL/SQL, generación de diagramas de bases de datos (ERD)...
Aunque viene preparada principalmente para trabajar con bases de datos Oracle, sirve también para Access, MySQL y SQLServer. Vamos a ver cómo configurarla para este último SGBD. El proceso debería ser equivalente para MySQL, pero a mí no me miren, que no lo he probado.
Como siempre que vamos a trabajar con conexiones a bases de datos, lo primero que tenemos que hacer es localizar el driver adecuado. En nuestro caso necesitamos el fichero jtds-1.2.5.jar; no hay más que pedírselo a Google. Una vez que lo tengamos en nuestro equipo, lo ubicaremos en la ruta jdbc/lib, dentro del directorio de instalación de SQLDeveloper (por ejemplo, C:\Archivos de programa\SqlDeveloper). Para referenciarlo desde la herramienta, accederemos a la opción del menú Herramientas - Preferencias, y dentro, a la sección Base de Datos - Controladores JDBC de Terceros. Ahí añadimos una entrada, y seleccionamos el jar.
Una vez aceptado, podemos acceder al wizard de creación de una nueva conexión. Tras introducir el nombre que queramos darle a la conexión y el usuario/password, tenemos una serie de pestañas que se corresponden con los distintos SGDB. Seleccionamos SQLServer, e indicamos el host y puerto, que normalmente será el 1433. Pulsamos entonces el botón Recuperar Base de Datos. Si la configuración es correcta, nos aparecerán las distintas bases de datos accesibles por el usuario. No tenemos más que seleccionar la que corresponda, y guardar los cambios.
En la lista de conexiones veremos una que destacará sobre el resto por tener el logotipo de Windows, lo que nos indica que es una conexión a SQLServer. No tenemos más que desplegarla y podremos comenzar a trabajar con la base de datos.
viernes, 4 de junio de 2010
Comillas en cadenas en Oracle - Operador q
Cuando trabajamos con bases de datos Oracle, el carácter comilla simple ( ' ) es utilizado como delimitador de cadenas de texto. No es raro encontrarnos con la necesidad de utilizar una cadena que contenga este carácter, y para estos casos podemos utilizar una solución muy simple, que consiste en duplicar la comilla para que sea interpretada como parte de la cadena, y no como delimitador:
'El carácter ' delimita las cadenas', sería interpretado como una cadena 'El carácter ', y el resto se tomaría como comandos, por lo que tendríamos un error.
'El carácter '' delimita las cadenas', al duplicar la comilla, Oracle la interpretará como un carácter, e interpretaría la cadena: 'El carácter ' delimita las cadenas'.
Esta es la solución más conocida, pero existe una alternativa, que es la utilización del operador q (como siempre del inglés, quote). Este operador permite indicar que las comillas de la cadena a la que se aplica son texto, y no delimitadores. Su utilización sigue el formato: q'd cadena d', donde d es un delimitador, para el que se pueden usar cualquier carácter (he probado con números, letras y signos de puntuación, y todos son válidos), aunque lo normal es utilizar #, [] o {}, para mayor legibilidad.
Dos formas equivalentes de obtener una cadena con el nombre entre comillas:
select 'La persona ''' || nombre || ''' seleccionada' from persona;
select q'{La persona '}' || nombre || q'{' seleccionada}' from persona;
¡A cual más clara!
domingo, 9 de mayo de 2010
"Metaconsultas" Oracle
Bueno, el término digamos que me lo he inventado, porque no tengo idea de cómo se llaman, pero lo que pretendo presentar es una idea interesante cuando se trabaja con consultas SQL. Hablo de Oracle porque es el SGBD con el que suelo trabajar, pero sería extrapolable a cualquier otro.
Partamos del problema: supongamos que queremos actualizar todos los registros de una tabla cuyo valor para una determinada columna comience por una cadena concreta, a otra cadena (también concreta, claro). Dicho así suena extraño, pero pongamos que tenemos los siguientes valores en una tabla:
Nombre
Don Manuel
Don Joaquín
Doña Lucía
Don Carlos
Pues lo que queremos, sencillamente, es actualizar los valores "Don X" a "D. X".
A priori, cualquiera diría que lo que hay que hacer es una única sentencia update muy sencilla, pero en cuanto empecemos a pensar nos daremos cuenta de que no lo es tanto. Sí resultaría sencillo con una consulta para cada registro a modificar, pero escribirlas todas no es una solución válida, puesto que queremos un método que nos sirva para tablas de cualquier tamaño, y si tenemos un millón de registros, podríamos tardar un ratito curioso.
Pues aquí es donde entran las "metaconsultas", a las que denomino así porque son consultas que nos permiten generar otras consultas. Simplemente tenemos que ejecutar lo siguiente:
select 'update nombre_tabla set nombre_campo=''D. ' || nombre_campo || ''' where nombre_campo=''' || nombre_campo || ''';' from nombre_tabla where nombre_campo like 'Don %';
Y obtendremos como salida una serie de sentencias update que si copiamos y ejecutamos, realizarán la actualización deseada.
El ejemplo presentado es el más básico posible, pero conociendo esta técnica seguro que le encontraréis muchas utilidades.
Partamos del problema: supongamos que queremos actualizar todos los registros de una tabla cuyo valor para una determinada columna comience por una cadena concreta, a otra cadena (también concreta, claro). Dicho así suena extraño, pero pongamos que tenemos los siguientes valores en una tabla:
Nombre
Don Manuel
Don Joaquín
Doña Lucía
Don Carlos
Pues lo que queremos, sencillamente, es actualizar los valores "Don X" a "D. X".
A priori, cualquiera diría que lo que hay que hacer es una única sentencia update muy sencilla, pero en cuanto empecemos a pensar nos daremos cuenta de que no lo es tanto. Sí resultaría sencillo con una consulta para cada registro a modificar, pero escribirlas todas no es una solución válida, puesto que queremos un método que nos sirva para tablas de cualquier tamaño, y si tenemos un millón de registros, podríamos tardar un ratito curioso.
Pues aquí es donde entran las "metaconsultas", a las que denomino así porque son consultas que nos permiten generar otras consultas. Simplemente tenemos que ejecutar lo siguiente:
select 'update nombre_tabla set nombre_campo=''D. ' || nombre_campo || ''' where nombre_campo=''' || nombre_campo || ''';' from nombre_tabla where nombre_campo like 'Don %';
Y obtendremos como salida una serie de sentencias update que si copiamos y ejecutamos, realizarán la actualización deseada.
El ejemplo presentado es el más básico posible, pero conociendo esta técnica seguro que le encontraréis muchas utilidades.
Suscribirse a:
Entradas (Atom)

