La instrucción SQL Insert y su difícil legibilidad

En ocasiones quedo asombrado (para mal) de que se haya aceptado como un estándar universal ciertas operaciones o instrucciones. Una de estas operaciones que me parece increíble que haya podido llegar a ser un estándar es la instrucción SQL para realizar inserciones.

Si pudiéramos calificar la maquiavélica invención de la instrucción de inserción, yo lo pondría entre el invento del paraguas (un instrumento diseñado para sacarle los ojos a los viandantes) y la invención de la batamanta (un invento diseñador para … aun no lo se).

Por alguna razón, el que diseñó esta instrucción quizá pensó que los programadores estabamos lo suficientemente aburridos y no teníamos otra cosa mejor que hacer que estar contando las posiciones para encajar los nombres de los campos que lleva la inserción y los valores correspondientes que van asociados.

Una instrucción a partir de diez campos empieza a ser incomprensible a simple vista, sobretodo si lo que se insertan son valores numéricos o valores que por su valor pueden ser cualquier cosa.

insert into tabla (campoN, campoE, campoF, campoC, campoL, campoQ, campoR, campoT, campoY, campoH, campoG campoS, campoW, campoZ, campoP) values(6,2,5,3,4,3,7,6,8,32,4,6,8,4,3)

¿Quién sabe decirme, sin contar posiciones, que campo se rellena con el valor 32?

Pero el problema no acaba aquí. En ocasiones cuando tenemos que insertar datos a veces hay que rellenar el dato y otras veces no en función de si se cumplen unas condiciones. Si generamos la consulta de manera dinámica nos topamos con el problema de la obligación de tener, no una, sino dos cadenas donde ir concatenando.

Si lo que utilizas son las consultas preparadas para solventar el problema del SQLInyection igualmente es necesario contar en que posición se encuentra el ‘?’ correspondiente e introducir el dato mediante una instrucción posterior.

Utilizar generadores de instrucciones SQL

Una de las soluciones ideales es utilizar un generador de instrucciones que nos transforme un modo de trabajo más fácil a consultas SQL. En lenguajes como Java existe JPA como estándar o puedes utilizar un ORM aunque tiene el pequeño inconveniente que hay que añadir dependencias JAR a tu proyecto. En Java también puedes utilizar ResultSet en modo update de manera que puedes modificar el ResultSet y luego ejecutar la instrucción update.

En el caso de Visual Studio también tienes ORMs como NHibernate o Entity Framework 4.x. También es posible evitar escribir los SQLs utilizando ADO.NET utilizando sqlAdapter. En PHP también dispones de ORMs como PHP Object Generator, Doctrine o ADOdb Active Record. Todas estas opciones son las ideales para el almacenamiento de datos si no quieres lidiar con el SQL directamente.

Crearse un conversor SQL

Un problema menor que tienen estas consultas es que no puedes ver la consulta final hasta que no la ha ejecutado. En ocasiones cuando se está depurando interesa ver la cadena que se va a ejecutar antes de que se ejecute. Esto viene bien para llevar mayor control de lo que se ejecuta en la base de datos mientras depuras y no tener que recurrir al log como ocurre con Hibernate.

Para ello una propuesta es crear un lenguaje más fácil de fabricar y de entender y al final ejecutarlo en un conversor a SQL.

Imagina que en la anterior consulta estuviese diseñada de esta manera:

campoN=6, campoE=2, campoF=5, campoC=3, campoL=4, campoQ=3, campoR=7, campoT=6, campoY=8, campoH=32, campoG= 4, campoS=6, campoW=8, campoZ=4, campoP=3

¿Y ahora? ¿que campo se rellena con el valor 32?

Pues bien, hacer este algoritmo es bastante sencillo si lo que deseas almacenar no tiene selects anidados. Además la cadena es igual que el utilizado para hacer updates por lo que se podría aprovechar el trabajo de uno para hacer también las modificaciones.

Aquí tenéis un pequeño algoritmo en Java que convierte la anterior cadena en una inserción. Lo que realiza es crear dos cadenas de manera que a partir que rellena la parte de los nombres hasta que encuentra un igual ‘=’ luego rellena el valor hasta encontrar una coma ‘,’ fuera de comillas simples

public class ConversorSQL {
     public String convertir(String tabla, String valores){
          String primeraParte="" , segundaParte="" ;
        
          boolean cargandoNombre=true, comillas=false;
          String separador="" , dato="", nombre="" ;

for (int i=0;i<valores.length();i++){ //Recorre la cadena caracter a caracter char caracter=valores.charAt(i); //Obtiene el caracter if (cargandoNombre){ //Si esta rellenando el nombre if (caracter=='='){ //Si encuentra el = cargandoNombre=false; //Pasa a rellenar los valores comillas=false; //Indicador de comillas para leer cadenas }else{ nombre+=caracter; //Concatena el caracter } }else{ //Si esta rellenando el dato if (caracter=='\''){ //Si encuentra una comilla comillas=!comillas; //Alterna la detección de cadenas } if ((!comillas && caracter==',')){ //Si no esta en una cadena y encuentra una coma cargandoNombre=true; //Pasa a cargar el nombre primeraParte+=separador+nombre; //Concantena el nombre segundaParte+=separador+dato; //Concantena el valor separador="," ; //El separador será la coma nombre="" ; //Inicializa el nombre dato="" ; //Inicializa el valor }else if(i==valores.length()-1){ //Si ha llegado al final de la cadena dato+=caracter; //Concatena el caracter primeraParte+=separador+nombre; //Concatena el nombre segundaParte+=separador+dato; //Concatena el valor }else{ //Si sigue leyendo caracteres del valor dato+=caracter; //Concatena el caracter } } } //Devuelve la cadena de inserción return "insert into " + tabla + "(" + primeraParte + ") " + "values (" + segundaParte + ")" ; } //Ejemplo para probar el algoritmo. public static void main(String args[]){ String ejemplo="dato1=2,dato2='hola',dato3='prueba'''" ; ConversorSQL con=new ConversorSQL(); String sql=con.convertir("mitabla", ejemplo); System.out.println(sql); } }

Quizá te preguntes que ocurre con el SQLInyection . Uno de los motivos de realizar consultas preparadas es evitar la inyección deSQL. En el texto plano esta resuelto poniendo dos comillas simples. Es decir, si se desea insertar “dato‘” en la cadena habría que poner como valor nombre=‘dato’‘’.

Finalmente con este algoritmo ya podrás hacer más fácilmente consultas con más o menos valores según ciertas condiciones.


String datos=&#34;&#34;;
si (cumpleA) entonces datos+=&#34;campo1=1,&#34;;
si (cumpleB) entonces datos+=&#34;campo2=2,&#34;;
…
si (cumpleN) entonces datos+=&#34;;campoN=99999,&#34;;
String sql=obj.convertir(datos);

Más información

Información sobre Insert | Wiki

Información sobre JPA | JSR317

Información sobre ADO.NET | Msdn Microsoft

Portada de Genbeta