Cuando manejamos la conexión a un
DBMS (DataBase Management System) podemos mandar cualquier tipo de datos, no solo caracteres de texto. Aplicable a cualquier servidor de Base de Datos (Microsoft SQL Server, MySQL, PostgreSQL, Firebird, etc).
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