Es muy común que cuando empezamos a manejar datos de una fuente externa a VFP, es decir, a servidores de base de datos, nos encontremos con la limitación de que únicamente podemos mandar cadenas de texto en las consultas SQL.
Para apoyarnos un poco, tomaremos una típica consulta SQL...
SELECT iID, cClave, cCliente, cRegistro WHERE (iID > <NumeroInferior > ) AND (iID < < NumeroSuperior > ) FROM Cliente
Donde, obviamente, el "Numero Inferior" y "Numero Superior" serían datos variables a consultar, tomados de algún formulario de VFP.
Nuestro primer intento puede ser intentar formatear la cadena, de manera a que lo que se envía sea igual que la consulta deseada:
lcConsulta = " SELECT iID, cClave, cCliente, cRegistro "+ " WHERE (iID > " + STR(thisform.txtNumerInferior.Value) + ")"+ " AND (iID < " + STR(Thisform.txtNumeroSuperior.Value) + ")"+ " FROM Cliente "
Lo anterior funcionará, ya que igual, tenemos una cadena de texto y los valores, que ya han sido convertidos a cadena serán enviados tal cual a nuestro servidor.
Pero eso, al parecer, no es eficiente... Quizás lo podríamos hacer mejor, no? Pues si, como vimos en un artículo pasado, podríamos usar bloques TEXT ... ENDTEXT, para mejorar nuestra presentación:
WITH ThisForm lnNumeroInferior = .txtNumeroSuperior.Value lnNumeroSuperior = .txtNumerSuperior.Value ENDWITH TEXT TO lcConsulta NOSHOW TEXTMERGE SELECT iID, cClave, cCliente, cRegistro WHERE (iID > <<NumeroInferior>> ) AND (iID < <<NumeroSuperior >> ) FROM Cliente ENDTEXT
Funciona, si, también hace lo que deseamos, enviará los datos con los valores que queremos sin ningún problema. Pero, hagamos esto un poco más complicado: ¿Qué pasará cuando querramos hacer una búsqueda en base a un dato de tipo caracter? Cambiando un poco el ejemplo, buscaremos el conjunto de datos que cumpla con la condición de encontrar los datos de un cliente con base a la clave (string) del mismo:
SELECT iID, cCliente, cDireccion, dFechaAlta, yAdeudo FROM Cliente WHERE cClave = 'ALFKI'
Como vemos en la consulta, y en el caso específico de SQL Server, cuando se hace una query en base a datos de caracteres debemos encerrar dicho valor entre comillas sencillas, ¿sencillo? , ¿o no?
lcCliente = Thisform.txtCliente.Value TEXT TO lcConsulta NOSHOW TEXTMERGE SELECT iID, cClave, cCliente, cDireccion FROM Cliente WHERE cClave = '<<lcCliente>>' ENDTEXT
Una vez más, vemos que el ejemplo, también funciona, pero tiene un detalle: debemos formatear cada consulta de acuerdo a los requerimentos de nuestro servidor, y según sea el caso, podría que fuera diferente de un servidor a otro, además de que por lo que a mí respecta, pienso que es bastante engorroso y podría evitarse.
La Parametrización al rescate
La forma sencilla de no tener complicaciones con los distintos tipos de datos de los servidores, y los distintos tipos de datos de VFP, resulta en usar la Parametrización, ¿y que es esto?, consiste en anteponer un símbolo de interrogación justo antes de una variable:
lcCliente = Thisform.txtCliente.Value TEXT TO lcConsulta NOSHOW SELECT iID, cClave, cCliente, cDireccion FROM Cliente WHERE cClave = ?lcCliente ENDTEXT IF SQLExec(lnHandle, lcConsulta, lcCursor) > 0 *** ¿Manipular, mostrar los datos? *** SELECT (lcCursor) ELSE IF AERROR(laError) > 0 Messagebox("Error en la consulta: "+laError[2]) ELSE Messagebox("Error inesperado en la aplicación") ENDIF ENDIF
Como podrás notar, en el bloque TEXT...ENDTEXT hemos prescindido de la función de TEXTMERGE, además de las comillas sencillas, y en su lugar, sólo hemos puesto un símbolo de cierre de interrogación.
¿Qué estamos haciendo?, cuando usamos la Parametrización, delegamos la función de formateo de variables al driver ODBC, VFP se encargará de comunicarle qué tipo de datos es, y casi por arte de magia, hará la conversión pertinente. ¿Sencillo?, asi es, y funcionará con todos los tipo de datos de VFP (no será el caso de objetos, no confundir). Haz el intento ;-).
Nota al margen
En los ejemplos arriba escritos hemos hecho solo consultas de datos, pero aplicará a cualquiera de los comandos de tipo DML (Data Manipulation Language) de SQL, tales como INSERT, UPDATE, DELETE, etc.:
** Insertar datos TEXT TO lcInsert NOSHOW INSERT INTO Detalles (cClave, iProductoID, nCantidad, yPrecioVenta, dFechaVenta) VALUES (?lcClave, ?lnProductoID, ?lnCantidad, ?lyPreciodeVenta, ?ldFechaVenta) ENDTEXT IF SQLExec(lnHandle,lcInsert) > 0 *** Datos insertados con éxito *** Messagebox("Datos insertados con éxito") ELSE IF AERROR(laError) > 0 Messagebox("Error al insertar datos:"+laError[2]) ELSE Messagebox("Error inesperado!!") ENDIF ENDIF ** Actualizar un registro TEXT TO lcUpdate NOSHOW UPDATE Detalles SET nCantidad = ?lnCantidad WHERE ( iMovimientoID = ?lnMovimiento ) ENDTEXT IF SQLExec(lnHandle, lcUpdate) > 0 *** Actualizados con exito *** Messagebox("Datos actualizados con éxito") ELSE IF AERROR(laError) > 0 Messagebox("Error al actualizar el registro:"+laError[2]) ELSE Messagebox("Error inesperado!!") ENDIF ENDIF ** Borrar registro(s) TEXT TO lcDelete NOSHOW DELETE FROM Detalles WHERE (iFacturaID = ?lnFactura) ENDTEXT llSucess = .F. IF VERSION(2) >= 9 *** Usar las nuevas características de VFP9 para conocer los registros afectados llSucess = SQLExec(lnHandle,lcDelete,NULL,aRowsAffected) > 0 IF (llSucess) lcMessage = "Se han borrado "+STR(aRowsAffected[1,2] ) + ; " artículos de la Factura "+STR(lnFactura) ENDIF ELSE llSucess = SQLExec(lnHandle,lcDelete) > 0 lcMessage = "Se han borrado todos los articulos de la Factura "+str(lnFactura) ENDIF IF llSucess *** Registros borrados con exito *** Messagebox(lcMessage) ELSE IF AERROR(laError) > 0 Messagebox("Error al intentar borrar los datos:"+laError[2]) ELSE Messagebox("Error inesperado!!") ENDIF ENDIF
Espero que este artículo les sea de utilidad.
Espartaco Palma Martínez
Excelente, la verdad Yo no le había tomado importancia a ? siempre mandaba lista la cadena en lo que respecta a los valores
ResponderBorrarClarísima la explicación.Gracias!
ResponderBorrarBuena explicación Gracias.
ResponderBorrar