De DBF a SQL (34). Enviándole parámetros a la función SQLEXEC()

En el artículo anterior ya hemos visto que para enviarle algún comando al Servidor deberemos usar la función SQLEXEC() del Visual FoxPro.

El segundo parámetro de la función SQLEXEC() es el comando que deseamos enviar. Pero aquí hay varias alternativas, algunas peores y algunas mejores, tal y como veremos a continuación.

Listado 1. Una forma (incorrecta) de enviarle parámetros a la función SQLEXEC()

llComandoOK = SQLEXEC(_Screen.nHandle, "SELECT * FROM BANCOS") = 1

Hay programadores que envían comandos al Servidor de forma similar a la mostrada en el Listado 1. Eso está mal. ¿Por qué?

Porque si la función SQLEXEC() falló y queremos verificar cual fue el problema, deberíamos escribir nuevamente todo lo que está entre comillas, por ejemplo:

Listado 2. Para verificar cual comando se envió al Servidor

llComandoOK = SQLEXEC(_Screen.nHandle, "SELECT * FROM BANCOS") = 1
  
=MESSAGEBOX("SELECT * FROM BANCOS")

En un comando tan simple y tan sencillo como el mostrado en los listados 1 y 2 no sería problema pero algunos SELECT pueden ser muy largos (como ya has visto en otros artículos de este blog) y por lo tanto escribir nuevamente todo el segundo parámetro sería muy engorroso.

Una mejor alternativa sería escribir:

Listado 3. Guardando el comando en una variable

lcComando = "SELECT * FROM BANCOS"
  
llComandoOK = SQLEXEC(_Screen.nHandle, lcComando) = 1
  
=MESSAGEBOX(lcComando)

Ahora, el comando que se envía al Servidor previamente se guarda en una variable y es esa variable la que se usa en todos los casos.

Si el comando que deseamos enviar al Servidor es constante, como en los casos hasta aquí mostrados, ya está bien con lo que hicimos, no necesitamos más. Pero en muchos casos necesitamos enviarle al Servidor datos variables y allí ya se complica el asunto.

Listado 4. Enviando un comando con datos variables al Servidor

lnIdenti = 5
  
lcComando = "SELECT * FROM BANCOS WHERE BAN_IDENTI = " + TRANSFORM(lnIdenti)
  
llComandoOK = SQLEXEC(_Screen.nHandle, lcComando) = 1

=MESSAGEBOX(lcComando)

Al ejecutar el Listado 4. veremos algo similar a:

Captura 1. Viendo el comando que se envió al Servidor

En la Captura 1. vemos el comando que se envió al Servidor, cuando ese comando es muy largo hacerlo de esta forma nos resultará muy útil para buscar y encontrar errores en el texto enviado.

Y hablando de ese tema, cuando el texto a enviar es largo, es preferible utilizar la construcción TEXT…ENDTEXT.

Listado 5. Enviando comandos largos al Servidor

TEXT TO lcComando NOSHOW
  SELECT * FROM BANCOS WHERE BAN_IDENTI = 5
ENDTEXT
  
llComandoOK = SQLEXEC(_Screen.nHandle, lcComando) = 1
  
=MESSAGEBOX(lcComando)

Escribir lcComando = «algún texto aquí» tiene la desventaja de que ese texto no puede superar los 254 caracteres, en cambio si se usa la construcción TEXT…ENDTEXT se puede sobrepasar ese límite, sin ningún problema. Por lo tanto, es mejor acostumbrarse a usar siempre TEXT…ENDTEXT y así el texto siempre será aceptado, sin importar cuantos caracteres escribamos.

¿Y cómo haríamos para enviarle datos variables al Servidor?

Listado 6. Una forma para enviarle datos variables al Servidor

M.IDENTI = 5
  
TEXT TO lcComando NOSHOW
  SELECT * FROM BANCOS WHERE BAN_IDENTI = ?M.IDENTI
ENDTEXT
  
llComandoOK = SQLExec(_Screen.nHandle, lcComando) = 1
  
=MessageBox(lcComando)

Fíjate que dentro de la construcción TEXT…ENDTEXT usamos un símbolo de pregunta antes del nombre de la variable. Para que esto funcione, esa variable debe ser PRIVATE o debe ser PUBLIC. Como seguramente ya sabes, no se recomienda usar PUBLIC, por lo tanto lo mejor sería que usaras PRIVATE.

Al ejecutar el Listado 6. veremos algo similar a esto:

Captura 2. El comando que se envió al Servidor usando una variable

¿Cuál es el problema con esta forma de enviar comandos al Servidor? Que no se ve cual es el valor que se envió. En este caso vemos ?M.IDENTI pero no sabemos cual es el valor que está guardado en M.IDENTI

Listado 7. Viendo los valores de las variables enviadas al Servidor

M.IDENTI = 5
  
TEXT TO lcComando TEXTMERGE NOSHOW
  SELECT * FROM BANCOS WHERE BAN_IDENTI = <<M.IDENTI>>
ENDTEXT
  
llComandoOK = SQLExec(_Screen.nHandle, lcComando) = 1
  
=MessageBox(lcComando)

Al ejecutar el Listado 7. lo que veremos será:

Captura 3. Viendo las variables enviadas al Servidor

Fíjate que en el Listado 7. después del TEXT TO se escribió TEXTMERGE. ¿Por qué? Porque esa palabra TEXTMERGE es la que le indica al Visual FoxPro que debe evaluar el contenido de la variable. Pero además, se usaron los símbolos << y >> para indicarle cual es la variable.

Desde luego que podríamos tener muchas variables, por ejemplo:

Listado 8. Enviando varias variables al Servidor

M.TABLA   = "BANCOS"
M.COLUMNA = "BAN_NOMBRE"
M.IDENTI  = 5
  
TEXT TO lcComando TEXTMERGE NOSHOW
  SELECT
    <<M.COLUMNA>>
  FROM
    <<M.TABLA>>
  WHERE
    BAN_IDENTI = <<M.IDENTI>>
ENDTEXT
  
llComandoOK = SQLExec(_Screen.nHandle, lcComando) = 1
  
=MessageBox(lcComando)

Al ejecutar el Listado 8. lo que veríamos sería:

Captura 4. Enviando varias variables al Servidor y viendo sus contenidos

Y si preferimos que el SELECT no esté en varias líneas sino todo seguido, podríamos escribir:

Listado 9. Para que las cláusulas del SELECT se muestren una a continuación de la otra

M.TABLA   = "BANCOS"
M.COLUMNA = "BAN_NOMBRE"
M.IDENTI  = 5
  
TEXT TO lcComando TEXTMERGE NOSHOW PRETEXT 15
  SELECT
    <<M.COLUMNA>>
  FROM
    <<M.TABLA>>
  WHERE
    BAN_IDENTI = <<M.IDENTI>>
ENDTEXT
  
llComandoOK = SQLExec(_Screen.nHandle, lcComando) = 1
  
=MessageBox(lcComando)

Al ejecutar el Listado 9. lo que veremos sería:

Captura 5. Las cláusulas del SELECT una a continuación de la otra

Para que se muestre en una sola línea en la construcción TEXT…ENDTEXT se escribió PRETEXT 15.

El autor de este blog prefiere ver los comandos como se muestra en la Captura 4. pero hay gente que prefiere verlos como se muestra en la Captura 5. así que para satisfacer ambos gustos se mostraron las dos formas.

Conclusión:

La función SQLEXEC() nos permite enviar solicitudes al Servidor y nos devuelve el resultado de la solicitud. Tenemos varias maneras de enviar esas solicitudes, tal y como pudimos ver en este artículo.

Menos de la manera mostrada en el Listado 1., la cual jamás deberíamos usar, dependiendo de las circunstancias podríamos usar alguna de las otras alternativas.

Artículos relacionados:

De DBF a SQL (1). Introducción

De DBF a SQL (2). Reemplazando comandos

De DBF a SQL (3). Entendiendo a los cursores

De DBF a SQL (4). Ejemplos de usar SELECT con una sola tabla

De DBF a SQL (5). Las funciones agrupadas

De DBF a SQL (6). Agrupando filas

De DBF a SQL (7). Poniéndoles condiciones a los grupos

De DBF a SQL (8). Ordenando las filas

De DBF a SQL (9). Uniendo tablas

De DBF a SQL (10). Obteniendo las primeras filas de una tabla

De DBF a SQL (11). Relacionando una tabla con otra tabla

De DBF a SQL (12). Relacionando una tabla consigo misma

De DBF a SQL (13). Relacionando a varias tablas entre sí

De DBF a SQL (14). Más sobre el uso de DISTINCT

De DBF a SQL (15). Usando subconsultas

De DBF a SQL (16). Más sobre las subconsultas

De DBF a SQL (17). Ejemplos de subconsultas

De DBF a SQL (18). Paginación

De DBF a SQL (19). Hallando los porcentajes sobre un total

De DBF a SQL (20). Los operadores especiales de comparación

De DBF a SQL (21). La técnica de los dos cursores

De DBF a SQL (22). Optimizando el uso del operador de comparación especial IN

De DBF a SQL (23). Usando la función CAST()

De DBF a SQL (24). Buscando texto

De DBF a SQL (25).  Creando tablas y cursores

De DBF a SQL (26). INSERT, UPDATE, y DELETE, con subconsultas

De DBF a SQL (27). Entendiendo Cliente/Servidor

De DBF a SQL (28). Conectándose a un Servidor

De DBF a SQL (29). Las cadenas de conexión ODBC a los motores SQL más populares 

De DBF a SQL (30). Conectándose mediante ADO a las bases de datos

De DBF a SQL (31). Optimizando la escritura de los SELECT

De DBF a SQL (32). Mejorando la estética de los SELECT 

De DBF a SQL (33). Usando la función SQLEXEC()

El índice del blog VFPavanzado

 

 

 

 

 

 

 

 

 

De DBF a SQL (30). Conectándose mediante ADO a las bases de datos

El método más común para conectarse a las bases de datos SQL se llama ODBC (Open Database Connectivity, o en español: conectividad abierta a las bases de datos). Sin embargo, no es el único método que se puede emplear, tal y como vimos en artículos anteriores. Otro método que puede utilizarse se llama ADO (ActiveX Data Objects, o en español: objetos de datos ActiveX).

¿Qué es ADO?

Es un conjunto de objetos cuya finalidad es acceder a fuentes de datos de una manera uniforme. No solamente a bases de datos, también a planillas Excel, documentos de texto, archivos CSV, archivos XML, etc. Cualquier archivo que contiene datos podría eventualmente ser accedido usando ADO.

ADO surgió en 1996 con la idea de Microsoft de uniformizar el acceso a los datos.

¿Qué se puede hacer con ADO?

Todas las operaciones normales en una Base de Datos: crear tablas,  borrar tablas, insertar filas, modificar filas, borrar filas, consultar filas, etc.

¿Qué se necesita para usar ADO?

Lo primero, en conseguir un proveedor para el motor SQL que estás usando.  Si usas Firebird, encontrarás el que utiliza el autor de este blog en:

https://www.ibprovider.com/eng/documentation/firebird_interbase_odbc_drivers.html

También podrías usar IBOLE.DLL, que es muy bueno, o el que prefieras (porque hay varios más).

¿Cómo se utiliza ADO?

Primero, debes conectarte a la Base de Datos

Segundo, debes obtener un RecordSet, o sea un conjunto resultado con los datos que te interesan

Tercero, puedes manipular esos datos como desees

Cuarto, debes cerrar el RecordSet

Quinto, debes cerrar la conexión

Ejemplo. Conectarse a la Base de Datos y mostrar los nombres de los clientes

En nuestra Base de Datos tenemos una tabla llamada CLIENTES y queremos obtener los nombres de los mismos.

Listado 1. Mostrar los nombres de los clientes

CLEAR

CLOSE ALL

CLEAR ALL

LOCAL loSQL, loRecordSet

*--- Primero, se conecta a la Base de Datos

loSQL = CreateObject("ADODB.Connection")

WITH loSQL
  .Provider = "LCPI.IBProvider.3.Lite"
  .ConnectionString = "User=SYSDBA;password=masterkey;Data Source=E:\SQL\DATABASES\BLOG_DB.FDB;auto_commit=true"
  .Open()
ENDWITH

*--- Segundo, se crea un RecordSet

loRecordSet = CreateObject("ADODB.RecordSet")

*--- Tercero, se obtienen los datos que nos interesan

loRecordSet.Open("Select * From CLIENTES", loSQL)

*--- Cuarto, se procesan los datos obtenidos

DO WHILE !loRecordSet.EOF
  ? loRecordSet.Fields.Item("CLI_NOMBRE").Value
  loRecordSet.MoveNext()
ENDDO

*--- Quinto, se cierran y se eliminan los objetos que se habían creado

loRecordSet.Close()

loSQL.Close()

RELEASE loRecordSet, loSQL
Captura 1. Los nombres de los clientes

 

Como puedes ver, en la pantalla se mostraron los nombres de los 4 clientes cuyos datos estaban guardados en la tabla respectiva.

A diferencia de ODBC aquí no obtendrás un cursor para trabajar con él. Un cursor es una tabla .DBF temporal y desde mi punto de vista es más útil porque puede ser indexada, mostrada en un browse, etc. La forma de usar ADO me parece más antigua, más manual, aunque sobre gustos…

Desde luego que siempre puedes enviar el contenido de los recordsets a cursores o a tablas .DBF pero sería un trabajo adicional y ¿vale la pena? eso ya no lo sé porque dependerá de tus circunstancias.

Propiedades, métodos y eventos de ADO

ADO tiene muchísimas propiedades, métodos y eventos que puedes usar, en el ejemplo anterior se mostró solamente una pequeñísima parte de ellos. Si te interesa el tema hay varias páginas con información útil en Internet, una de ellas es la siguiente:

http://www.w3schools.com/ado/default.asp

Conclusión:

Si quieres, puedes usar ADO para conectarte a una Base de Datos pero yo hasta ahora no le he encontrado alguna ventaja. Quizás sea más rápido que ODBC y en ese caso sí podría justificarse conectarse mediante ADO, pero eso es algo que aún no he verificado.

Usar cursores es más rápido y más sencillo (para mí, claro) que estar obteniendo los datos fila por fila. Esto lo notarás principalmente cuando quieras imprimir informes porque mientras un cursor o una tabla .DBF ya están preparados para ser usados en informes, los recordsets no lo están y deberán prepararse previamente y habría que ver si vale la pena el esfuerzo.

De todas maneras, siempre es bueno tener varias alternativas para hacer cualquier cosa y disponer de ADO aumenta nuestras posibilidades de conexión a las bases de datos, así que bienvenido.

Artículos relacionados:

 

De DBF a SQL (1). Introducción

De DBF a SQL (2). Reemplazando comandos

De DBF a SQL (3). Entendiendo a los cursores

De DBF a SQL (4). Ejemplos de usar SELECT con una sola tabla

De DBF a SQL (5). Las funciones agrupadas

De DBF a SQL (6). Agrupando filas

De DBF a SQL (7). Poniéndoles condiciones a los grupos

De DBF a SQL (8). Ordenando las filas

De DBF a SQL (9). Uniendo tablas

De DBF a SQL (10). Obteniendo las primeras filas de una tabla

De DBF a SQL (11). Relacionando una tabla con otra tabla

De DBF a SQL (12). Relacionando una tabla consigo misma

De DBF a SQL (13). Relacionando a varias tablas entre sí

De DBF a SQL (14). Más sobre el uso de DISTINCT

De DBF a SQL (15). Usando subconsultas

De DBF a SQL (16). Más sobre las subconsultas

De DBF a SQL (17). Ejemplos de subconsultas

De DBF a SQL (18). Paginación

De DBF a SQL (19). Hallando los porcentajes sobre un total

De DBF a SQL (20). Los operadores especiales de comparación

De DBF a SQL (21). La técnica de los dos cursores

De DBF a SQL (22). Optimizando el uso del operador de comparación especial IN

De DBF a SQL (23). Usando la función CAST()

De DBF a SQL (24). Buscando texto

De DBF a SQL (25).  Creando tablas y cursores

De DBF a SQL (26). INSERT, UPDATE, y DELETE, con subconsultas

De DBF a SQL (27). Entendiendo Cliente/Servidor

De DBF a SQL (28). Conectándose a un Servidor

De DBF a SQL (29). Las cadenas de conexión ODBC a los motores SQL más populares 

De DBF a SQL (30). Conectándose mediante ADO a las bases de datos

De DBF a SQL (31). Optimizando la escritura de los SELECT

De DBF a SQL (32). Mejorando la estética de los SELECT 

De DBF a SQL (33). Usando la función SQLEXEC()

De DBF a SQL (34). Enviándole parámetros a la función SQLEXEC()

El índice del blog VFPavanzado

 

De DBF a SQL (28). Conectándose a un Servidor

En el artículo:

De DBF a SQL (27). Entendiendo Cliente/Servidor

ya hemos visto las grandes ventajas que tendremos si usamos esa tecnología. Ahora veremos un ejemplo práctico, para entender mejor.

¿Que se necesita para conectarse a un Servidor?

  • Que el Servidor esté instalado en una computadora. Puede ser la propia computadora o alguna otra computadora, tanto en una red local como en una red remota. Pero debe estar instalado, sí o sí.
  • Que el Cliente esté instalado en la computadora desde la cual se realizará la conexión. Si en una computadora no se instaló el Cliente, entonces desde esa computadora no se podrá conectar con el Servidor.
  • Que un driver de comunicación esté instalado en la computadora del Cliente. Para que el Cliente pueda comunicarse con el Servidor y para que el Servidor pueda comunicarse con el Cliente, se necesita de un canal que los enlace. Hay muchos, el más usado es el ODBC.
  • Si usamos ODBC, escribir una cadena de conexión. Hay de dos tipos: a) con DSN y, b) sin DSN.

La cadena de conexión de ODBC

La cadena de conexión depende del motor SQL que utilices. Las diferencias en las sintaxis no son grandes, pero existen y deben tomarse en cuenta porque cualquier diferencia entre lo que deberías haber escrito y lo que realmente escribiste hará que la conexión sea rechazada.

Cadena de conexión sin DSN

Si te conectas a una Base de Datos con DSN, tu cadena de conexión será más corta pero previamente debiste haberte tomado el trabajo de crear el DSN en esa computadora. En general, no se justifica hacerlo y por ello el autor de este blog siempre se conecta sin DSN.

Listado 1. Conectándose a un Servidor mediante ODBC y sin usar DSN

lcCadenaConexion = "DRIVER={Firebird/Interbase(r) driver};" ;
                 + "USER=SYSDBA;" ;
                 + "PASSWORD=masterkey;" ;
                 + "ROLE=;" ;
                 + "DATABASE=D:\CONTABILIDAD\BASESDATOS\GRACIELA.FDB;"

En el Listado 1. vemos un ejemplo típico de cadena de conexión sin usar DSN. Allí especificamos:

  1. Cual es el driver que usaremos para conectar al Cliente con el Servidor. Eso también implica cual es el motor SQL que usaremos. En este ejemplo es Firebird pero podría ser cualquier otro: MySql, MariaDb, Postgre, SQL Server, Oracle, etc.
  2. El nombre del usuario. Para conectarse a una Base de Datos siempre se debe escribir el nombre del usuario. Es imposible realizar la conexión si no se escribe ese dato.
  3. La contraseña del usuario. Todo usuario debe tener una contraseña o password, sí o sí. No existe la posibilidad de conectarse sin escribir una contraseña.
  4. El rol del usuario. Un rol es un grupo de usuarios que tienen los mismos derechos (también llamados permisos o privilegios) en una Base de Datos. Este dato es opcional, si no se lo escribe entonces el usuario solamente tendrá sus propios derechos (permisos/privilegios) pero no los derechos (permisos/privilegios) que tiene algún rol.
  5. La Base de Datos. Dentro de un Servidor se pueden tener muchas bases de datos y se debe escribir a cual de esas bases de datos el usuario se quiere conectar.

NOTA: Los espacios en blanco son muy importantes. No debes colocarlos ni antes ni después del signo igual porque ese pequeño detalle en algunos casos hará que la conexión sea rechazada.

Realizando la conexión

Para intentar la conexión a la Base de Datos que especificamos en el Listado 1. simplemente debemos escribir:

Listado 2. Intentando conectar a la Base de Datos

lnHandle = SQLStringConnect(lcCadenaConexion)

La función SQLStringConnect() del Visual FoxPro es la que deberemos usar cuando nuestra cadena de conexión es sin DSN. Fíjate que guardamos el valor que devuelve esa función en una variable, llamada en este caso lnHandle (ese nombre es un ejemplo, tú puedes darle el nombre que prefieras). ¿Y por qué guardamos el resultado de la función en una variable? Porque a partir de este momento todas las acciones que realicemos en la Base de Datos se harán usando esa variable.

La primera acción debe ser, evidentemente, verificar si el intento de conexión tuvo éxito o no lo tuvo.

¿Y cómo sabemos si la conexión se realizó exitosamente?

Verificando el valor de la variable. Si ese valor es 1 (uno), fue exitosa, si fue -1 (menos uno), ocurrió algún error. ¿Cuál error? Eso lo podremos averiguar usando la función AERROR().

Si la conexión fue exitosa, entonces ya podremos realizar cualquier operación que deseemos en la Base de Datos. Desde luego que si no tenemos permiso (derecho/privilegio) para realizar esa operación, el resultado será un fracaso.

Un pequeño programita para realizar una consulta a una Base de Datos

Captura 1. Conectándose a la Base de Datos y realizando una consulta

¿Qué hicimos en la Captura 1.?

  1. Intentamos la conexión a la Base de Datos
  2. Si la conexión tuvo éxito, intentamos consultar a la tabla BANCOS
  3. Si la consulta tuvo éxito, el contenido de la tabla BANCOS se guardará en el cursor cuyo nombre es MICURSOR. Mostramos el contenido de ese cursor.
  4. Si la consulta no tuvo éxito, el problema pudo estar en el comando que quisimos ejecutar (el comando SELECT, en este ejemplo). Los datos de ese error lo guardamos en el array laError y le mostramos al usuario el problema que ocurrió.
  5. Nos desconectamos de la Base de Datos. Eso siempre debemos hacer cuando ya no necesitemos estar conectados. La función SQLDisconnect() es la que se encarga de esa tarea.
  6. Si el intento de conexión a la Base de Datos no tuvo éxito, le mostramos un mensaje al usuario.

Fíjate que la función que usamos para ejecutar un comando en la Base de Datos se llama SQLEXEC(). El primer parámetro que recibe debe ser la variable que usamos al conectarnos a la Base de Datos. ¿Por qué? porque podríamos estar conectados a varias bases de datos al mismo tiempo y el Visual FoxPro necesita saber a cual de ellas nos estamos refiriendo. Por ejemplo, podrías tener lnHandle1, lnHandle2, lnHandle3, para conectarte a 3 bases de datos distintas y al mismo tiempo.

Análogamente, la función SQLDISCONNECT() también recibe como parámetro a la variable que se obtuvo al conectarse a la Base de Datos. ¿Por qué? porque si te conectaste a varias bases de datos, debe saber de cual de ellas quieres desconectarte. NOTA: Si escribes SQLDISCONNECT(0) te desconectarás de todas las bases de datos. Ese número cero significa que quieres desconectarte de todas.

Captura 2. El resultado de la consulta a la tabla BANCOS

Si la conexión a la Base de Datos tuvo éxito y la consulta a la tabla de BANCOS también tuvo éxito, entonces obtendremos el resultado buscado, tal y como se muestra en la Captura 2.

Conclusión:

Conectarse a una Base de Datos usando la tecnología Cliente/Servidor no es algo difícil de realizar, tal y como pudiste ver en este artículo.

Como habrás notado, al realizar una operación (un SELECT, en este caso), el resultado se guarda en un cursor. Ese cursor es local, lo que hagas en él no afecta a la tabla original, la tabla de la cual proviene. A ese cursor le puedes agregar filas, modificar filas, o borrar filas, que la tabla original no se verá afectada.

Es como hacer la fotocopia de un libro. En la fotocopia puedes escribir lo que se te antoje que el libro no tendrá lo que escribiste en la fotocopia.

El trabajar con cursores es una gran ventaja, porque puedes hacer lo que quieras con ellos sin afectar a las tablas originales. Solamente cuando necesites actualizar  a las tablas originales es que los contenidos de los cursores se trasladarán a ellas.

Artículos relacionados:

De DBF a SQL (1). Introducción

De DBF a SQL (2). Reemplazando comandos

De DBF a SQL (3). Entendiendo a los cursores

De DBF a SQL (4). Ejemplos de usar SELECT con una sola tabla

De DBF a SQL (5). Las funciones agrupadas

De DBF a SQL (6). Agrupando filas

De DBF a SQL (7). Poniéndoles condiciones a los grupos

De DBF a SQL (8). Ordenando las filas

De DBF a SQL (9). Uniendo tablas

De DBF a SQL (10). Obteniendo las primeras filas de una tabla

De DBF a SQL (11). Relacionando una tabla con otra tabla

De DBF a SQL (12). Relacionando una tabla consigo misma

De DBF a SQL (13). Relacionando a varias tablas entre sí

De DBF a SQL (14). Más sobre el uso de DISTINCT

De DBF a SQL (15). Usando subconsultas

De DBF a SQL (16). Más sobre las subconsultas

De DBF a SQL (17). Ejemplos de subconsultas

De DBF a SQL (18). Paginación

De DBF a SQL (19). Hallando los porcentajes sobre un total

De DBF a SQL (20). Los operadores especiales de comparación

De DBF a SQL (21). La técnica de los dos cursores

De DBF a SQL (22). Optimizando el uso del operador de comparación especial IN

De DBF a SQL (23). Usando la función CAST()

De DBF a SQL (24). Buscando texto

De DBF a SQL (25).  Creando tablas y cursores

De DBF a SQL (26). INSERT, UPDATE, y DELETE, con subconsultas

De DBF a SQL (27). Entendiendo Cliente/Servidor

De DBF a SQL (29). Las cadenas de conexión ODBC a los motores SQL más populares 

De DBF a SQL (30). Conectándose mediante ADO a las bases de datos

De DBF a SQL (31). Optimizando la escritura de los SELECT

De DBF a SQL (32). Mejorando la estética de los SELECT 

De DBF a SQL (33). Usando la función SQLEXEC()

De DBF a SQL (34). Enviándole parámetros a la función SQLEXEC()

El índice del blog VFPavanzado

 

De DBF a SQL (27). Entendiendo Cliente/Servidor

Hasta ahora, todo lo que hemos hecho en esta serie de artículos podíamos realizarlos con las tablas .DBF, nativas del Visual FoxPro. Eso está muy bien y podríamos quedarnos allí porque como habrás comprobado si pusiste en práctica los ejemplos con tus propias tablas, hay una muy notoria ganancia en velocidad. Escribes menos y obtienes los resultados mucho más rápidamente. Está muy bueno eso ¿verdad?. Pero no nos  quedaremos allí sino que daremos un gran salto hacia adelante y nos adentraremos en una tecnología muy distinta a la tradicionalmente usada en Visual FoxPro pero que a su vez es extremadamente poderosa. Su nombre es Cliente/Servidor y es más o menos el equivalente a que un jugador de fútbol de un club sudamericano de mediano porte vaya a jugar al Barcelona de España o al Real Madrid o algo así. Es totalmente otro nivel de juego.

Entonces, ¿de qué se trata Cliente/Servidor?

Lo primero que debes saber es que esta es una metodología para acceder a los datos desde varias computadoras, en otras palabras, desde una red de computadoras. Aunque puedes hacer aplicaciones Cliente/Servidor que sean monousuario no fue pensado para eso sino para aplicaciones multiusuario.

En las aplicaciones antiguas (o sea, las tradicionales del VFP) los programas “tocaban” físicamente a los datos, o sea que por un lado estaba el programa, por el otro lado estaban los datos que se guardaban dentro de los archivos y no había algo entre ellos que los separara.

Programa <——–> Archivo de datos

Esto funcionaba, pero con muuuchos problemas potenciales, por ejemplo un usuario estaba modificando un registro que otro usuario también estaba modificando. O un usuario estaba borrando un registro que otro usuario necesitaba en su informe. Eso causaba muchos inconvenientes. Mientras no había algo mejor los usuarios debían conformarse con eso … pero ahora tenemos Cliente/Servidor.

¿Cómo funciona Cliente/Servidor?

En esta metodología los programas jamás “tocan” físicamente a los datos, aunque quieran hacerlo no podrán, es totalmente imposible. En lugar de eso, los programas se comunican con el Cliente, el Cliente le envía una petición al Servidor, y es el Servidor quien procesa esa petición y le envía la respuesta al Cliente y el Cliente se la envía de regreso al programa.

Programa —–> Cliente —–>Servidor

Servidor —–> Cliente —–> Programa

Como ves, el camino es mucho más largo pero también es muchísimo más seguro y confiable. El Cliente es el intermediario. Nada puede hacerse sin él.

Entonces, el Servidor se instala en una computadora. ¿En cuál computadora? En la computadora donde estarán alojadas todas las bases de datos.

El Cliente se instala en todas las demás computadoras, o sea en las computadoras que usarán los usuarios.

¿Y cómo se comunica el Cliente con el Servidor, y viceversa? Para que la comunicación entre ellos sea posible se necesita de una canal de comunicación. Hay muchos canales de comunicación, entre ellos: ADO, JDBC, ODBC, .NET, .PYTHON, etc. El más usado es el ODBC, pero tú puedes optar por cualquier otro, si así lo prefieres.

Cuando un programa necesita algo se lo pide al Cliente y éste se lo pide al Servidor. ¿Qué puede necesitar un programa? Veamos algunos ejemplos:

  • Grabar la venta que corresponde a la Factura Nº 12345, con fecha de hoy, le vendimos a Juan Pérez y el monto de la venta fue de 1.250 dólares
  • Grabar la cobranza que hoy le hicimos a María Benítez, según el Recibo Nº 785 y por un monto de 420 dólares
  • Cambiar el precio de venta de los monitores Toshiba de 21″, el nuevo precio es 100 dólares
  • Ver todas las ventas que hicimos el mes pasado
  • Ver los nombres de todos los vendedores que hoy hicieron ventas por más de 500 dólares

En todos estos casos el programa se lo pide al Cliente, el Cliente se lo pide al Servidor y el Servidor trata de cumplir con el pedido. Si pudo cumplir se lo indica al Cliente enviándole un número 1. Si no pudo cumplir le envía el número -1. Y si aún no terminó de procesar el pedido le envía un 0 (cero) y así el Cliente sabe que el Servidor todavía está trabajando.

Por lo tanto, cuando recibe un 1 el Cliente sabe que su petición tuvo éxito. Y si recibe un -1 el Cliente sabe que su petición falló. Súper fácil ¿verdad?

Si la petición tuvo éxito entonces el Cliente recibe un “cursor”. ¿Y qué es un cursor? ya lo sabes, porque ya lo hemos descrito en artículos anteriores de esta serie. Un cursor es una tabla de memoria, una tabla virtual, una copia del contenido que hay en el Servidor.

Eso significa que si modificas el cursor la Base de Datos ni se entera, su contenido queda exactamente igual a como estaba antes. ¿Por qué? porque el cursor es una copia de lo que está dentro de la Base de Datos (es como fotocopiar un libro, si escribes sobre la fotocopia el libro original continúa igual, allí no verás lo que escribiste en la fotocopia. En Cliente/Servidor siempre trabajas con fotocopias y jamás puedes tocar al libro original).

Resumiendo:

  • El Servidor se instala en una sola computadora (en la que se guardarán todas las bases de datos)
  • El Cliente se instala en muchas computadoras (en las que trabajarán los usuarios)
  • Para que el Cliente y el Servidor puedan comunicarse entre sí se necesita de un canal de comunicación entre ellos (por ejemplo el driver ODBC)
  • El Cliente envía peticiones al Servidor, el Servidor procesa esas peticiones y luego le envía el resultado al Cliente
  • El Cliente sabe que su petición tuvo éxito si recibe como respuesta el número 1. Si la respuesta es -1 entonces la petición falló. Si la respuesta es 0 entonces el Servidor aún continúa procesando los datos
  • Si el Cliente recibió un 1 porque la petición tuvo éxito entonces también recibe un “cursor”, o sea una tabla de memoria, donde se encuentra la respuesta enviada por el Servidor (por ejemplo, los nombres de todos los vendedores que hoy vendieron por un monto superior a 500 dólares)
  • Ni la aplicación (el sistema de contabilidad, de ventas, de tesorería, de cobranzas, etc.) ni el Cliente jamás tocan a los datos, solamente el Servidor puede hacer eso.
  • El Servidor sólo toca a los datos si recibe un pedido del Cliente para hacerlo. Si el Cliente no se lo pide, el Servidor jamás toca a los datos.

Ventajas de usar Cliente/Servidor

Control centralizado: Para poder acceder a una Base de Datos se necesita el nombre de un usuario y su contraseña y los permisos (también llamados derechos o privilegios) correspondientes. Una tabla .DBF puede abrirla cualquiera que conozca aunque sea un poquito de Visual FoxPro. Una tabla SQL no. Solamente podrá abrirla si tiene autorización para hacerlo.

Ocultamiento: Para abrir una tabla .DBF se requiere conocer en cual computadora se encuentra, en cual carpeta de esa computadora, y cual es el nombre de esa tabla .DBF. En buenos motores como Firebird SQL, no es necesario tener toda esa información. Podrías pedir conectarte a MISERVIDOR/MIBASEDATOSCOLEGIO y si previamente el Administrador de la Base de Datos definió a cual computadora se la conoce como MISERVIDOR y a cual Base de Datos de esa computadora se la conoce como MIBASEDATOSCOLEGIO, podrías realizar la conexión sin tener la menor idea del nombre real de la Base de Datos (podría ser DARWIN.FDB, por ejemplo) ni cual es la computadora (podría ser una computadora local, la que tiene el IP 192.168.0.25, por ejemplo; o podría ser una computadora remota, la que tiene el IP 181.120.90.145, por ejemplo).

Seguridad: Como consecuencia directa de los dos puntos anteriores, la seguridad que se puede obtener es muy grande. Si por ejemplo a un usuario solamente se le otorgó el derecho de hacer SELECT a la tabla ALUMNOS, él no podrá causarle un daño a la Base de Datos aunque lo intente y lo reintente. Primero, porque con un SELECT jamás podría causar daño, y segundo porque como ya vimos quizás ni siquiera sabe en cual computadora se encuentra la Base de Datos, ni en cual carpeta, ni cual es el nombre real de esa Base de Datos.

Velocidad: Los Servidores SQL están optimizados para conseguir una muy buena velocidad de respuesta. ¿Visual FoxPro te parece rápido? Si crees eso entonces es seguro que no has probado un buen motor SQL, tal como Firebird. El autor de este blog aún recuerda cuando un proceso que con tablas .DBF se demoraba más de 20 minutos en realizar, con Firebird finalizaba en 5 ó 6 segundos, es otro mundo.

Escalabilidad: Al mejorar el hardware de la computadora donde se encuentra el Servidor se puede conseguir un gran incremento en la velocidad de las respuestas.

Menos fallas: En las tablas .DBF un problema eterno es que pueden corromperse. Un corte de la energía eléctrica o de la conexión con el Servidor pueden dañar a las tablas de datos o a los archivos de índice. En un buen motor SQL (ya lo sabes: como Firebird) tal cosa no puede ocurrir. Aunque se corte la energía eléctrica 100 veces en un día, ninguna tabla de la Base de Datos será dañada.

Backups en cualquier momento: las tablas .DBF no se pueden copiar mientras están siendo usadas, primero debes cerrarlas y luego copiarlas. Con un buen motor SQL (y sí…Firebird) puedes realizar los backups cuando se te ocurra, ningún problema.

Mejor desarrollo de aplicaciones: como ya no debes escribir tanto código y además no debes perder tanto tiempo pensando en los algoritmos correctos, y haciendo un larguísimo proceso de prueba/error entonces puedes concentrarte en lo realmente importante: que tu aplicación se vea mejor, sea más fácil de usar, sea más completa. Esto te posibilitará aumentar las ventas y por lo tanto, ganar más dinero.

Conclusión:

Cambiar de la forma tradicional de programar a Cliente/Servidor no es algo que se puede conseguir de la noche a la mañana pero las ventajas de realizar ese cambio son muy grandes y deberían muy seriamente ser analizadas.

Porque en resumidas cuentas lo que se conseguirá será trabajar menos y obtener mejores resultados.

Artículos relacionados:

De DBF a SQL (1). Introducción

De DBF a SQL (2). Reemplazando comandos

De DBF a SQL (3). Entendiendo a los cursores

De DBF a SQL (4). Ejemplos de usar SELECT con una sola tabla

De DBF a SQL (5). Las funciones agrupadas

De DBF a SQL (6). Agrupando filas

De DBF a SQL (7). Poniéndoles condiciones a los grupos

De DBF a SQL (8). Ordenando las filas

De DBF a SQL (9). Uniendo tablas

De DBF a SQL (10). Obteniendo las primeras filas de una tabla

De DBF a SQL (11). Relacionando una tabla con otra tabla

De DBF a SQL (12). Relacionando una tabla consigo misma

De DBF a SQL (13). Relacionando a varias tablas entre sí

De DBF a SQL (14). Más sobre el uso de DISTINCT

De DBF a SQL (15). Usando subconsultas

De DBF a SQL (16). Más sobre las subconsultas

De DBF a SQL (17). Ejemplos de subconsultas

De DBF a SQL (18). Paginación

De DBF a SQL (19). Hallando los porcentajes sobre un total

De DBF a SQL (20). Los operadores especiales de comparación

De DBF a SQL (21). La técnica de los dos cursores

De DBF a SQL (22). Optimizando el uso del operador de comparación especial IN

De DBF a SQL (23). Usando la función CAST()

De DBF a SQL (24). Buscando texto

De DBF a SQL (25).  Creando tablas y cursores

De DBF a SQL (26). INSERT, UPDATE, y DELETE, con subconsultas

De DBF a SQL (28). Conectándose a un Servidor

De DBF a SQL (29). Las cadenas de conexión ODBC a los motores SQL más populares 

De DBF a SQL (30). Conectándose mediante ADO a las bases de datos

De DBF a SQL (31). Optimizando la escritura de los SELECT

De DBF a SQL (32). Mejorando la estética de los SELECT 

De DBF a SQL (33). Usando la función SQLEXEC()

De DBF a SQL (34). Enviándole parámetros a la función SQLEXEC()

El índice del blog VFPavanzado

 

De DBF a SQL (24). Buscando texto

Muchas veces necesitamos realizar búsquedas en un texto, por ejemplo para tener una lista de todas las personas cuyos apellidos empiezan con la letra «V». Para ello en SQL tenemos el operador LIKE.

LIKE puede usar dos «comodines»:

_ Significa que allí hay un carácter desconocido

% Significa que a continuación puede existir una cantidad desconocida de caracteres

Veamos algunos ejemplos:

Captura 1. La tabla de CLIENTES ordenada por los nombres de los clientes

Ejemplo 1. Ver a todos los clientes cuyos nombres empiezan con la letra «M»

Captura 2. Todos los clientes cuyos nombres empiezan con la letra «M»

Lo que hicimos en la Captura 2. fue pedirle que nos muestre a todos los clientes cuya primera letra del nombre es una «M» y a continuación puede existir una cantidad desconocida de caracteres.

Ejemplo 2. Ver a todos los clientes cuyos nombres empiezan con «EL»

Captura 3. Todos los clientes cuyos nombres empiezan con las letras «EL»

En este caso escribimos dos letras antes del símbolo de porcentaje, por lo tanto nos mostró solamente a aquellos clientes cuyos nombres empiezan con esas dos letras.

Ejemplo 3. Ver a todos los clientes que tienen una letra «S» en sus nombres

Captura 4. Todos los clientes que tienen una letra «S» en sus nombres

En la Captura 4. usamos dos veces al comodín «%», una vez antes de la letra «S» y otra vez después de la letra «S». Con eso le estamos diciendo al VFP que la letra «S» puede estar en cualquier lugar: al principio (como en «Silvia»), en el medio (como en «Isabel»), o al final (como en «Mercedes»).

Ejemplo 4. Ver a todos los clientes cuya segunda letra del nombre es una «E»

Captura 5. Todos los clientes cuya segunda letra del nombre es una «E»

Cuando queremos que el texto conocido se encuentre en una posición exacta usamos el guión bajo (_). En ese caso, como hay un guión bajo al principio, eso significa que el primer carácter puede ser cualquiera, luego debe haber una letra «E» y los siguientes caracteres pueden ser cualesquiera.

Ejemplo 5. Ver a todos los clientes cuya tercera letra sea una «R»

Captura 6. Todos los clientes cuya tercera letra del nombre es una «R»

En la Captura 6. hemos escrito dos guiones bajos y luego una letra «R» y luego el símbolo de porcentaje. Eso significa que el primer carácter puede ser cualquiera, el segundo carácter puede ser cualquiera, el tercer carácter debe ser una letra «R», y los restantes caracteres pueden ser cualesquiera.

Ejemplo 6. Ver a todos los clientes cuya segunda letra sea una «A» y cuya quinta letra sea una «E»

Captura 7. Los clientes cuya segunda letra es una «A» y la quinta letra es una «E»

En la Captura 7. mediante el uso de los guiones bajos le hemos indicado que la segunda letra debe ser una «A» y que la quinta letra debe ser una «E». Los demás caracteres pueden ser cualesquiera.

Ejemplo 7. Ver a todos los clientes cuya última letra del nombre es una «A»

Captura 8. Todos los clientes cuya última letra del nombre es una «A»

En la Captura 8. hemos obtenido los clientes que en la última letra de su nombre tienen una «A». También podríamos haber obtenido los que terminan con «NA» o con «ENA» o con lo que deseáramos.

Conclusión:

Para buscar caracteres o inclusive palabras enteras dentro de un texto podemos usar el operador LIKE. Éste acepta dos comodines: a) el guión bajo que reemplaza a un carácter y, b) el símbolo de porcentaje que reemplaza a cualquier cantidad de caracteres.

En los ejemplos de arriba hemos visto como ambos pueden ser usados. Desde luego que hay muchísimas más combinaciones posibles, puedes inclusive buscar palabras enteras, frases enteras, que pueden estar al principio, en el medio, al final, etc.

Artículos relacionados:

De DBF a SQL (1). Introducción

De DBF a SQL (2). Reemplazando comandos

De DBF a SQL (3). Entendiendo a los cursores

De DBF a SQL (4). Ejemplos de usar SELECT con una sola tabla

De DBF a SQL (5). Las funciones agrupadas

De DBF a SQL (6). Agrupando filas

De DBF a SQL (7). Poniéndoles condiciones a los grupos

De DBF a SQL (8). Ordenando las filas

De DBF a SQL (9). Uniendo tablas

De DBF a SQL (10). Obteniendo las primeras filas de una tabla

De DBF a SQL (11). Relacionando una tabla con otra tabla

De DBF a SQL (12). Relacionando una tabla consigo misma

De DBF a SQL (13). Relacionando a varias tablas entre sí

De DBF a SQL (14). Más sobre el uso de DISTINCT

De DBF a SQL (15). Usando subconsultas

De DBF a SQL (16). Más sobre las subconsultas

De DBF a SQL (17). Ejemplos de subconsultas

De DBF a SQL (18). Paginación

De DBF a SQL (19). Hallando los porcentajes sobre un total

De DBF a SQL (20). Los operadores especiales de comparación

De DBF a SQL (21). La técnica de los dos cursores

De DBF a SQL (22). Optimizando el uso del operador de comparación especial IN

De DBF a SQL (23). Usando la función CAST()

De DBF a SQL (25).  Creando tablas y cursores

De DBF a SQL (26). INSERT, UPDATE, y DELETE, con subconsultas

De DBF a SQL (27). Entendiendo Cliente/Servidor

De DBF a SQL (28). Conectándose a un Servidor

De DBF a SQL (29). Las cadenas de conexión ODBC a los motores SQL más populares 

De DBF a SQL (30). Conectándose mediante ADO a las bases de datos

De DBF a SQL (31). Optimizando la escritura de los SELECT

De DBF a SQL (32). Mejorando la estética de los SELECT 

De DBF a SQL (33). Usando la función SQLEXEC()

De DBF a SQL (34). Enviándole parámetros a la función SQLEXEC()

El índice del blog VFPavanzado

 

De DBF a SQL (23). Usando la función CAST()

En SQL existe una función muy útil llamada CAST(). Se utiliza para cambiar el tipo de una variable o columna por otro tipo.

Por ejemplo, en Visual FoxPro para cambiar a una variable que es de tipo carácter por otra que es de tipo numérico podemos usar la función VAL().

Listado 1. Convirtiendo una variable de carácter a numérica en Visual FoxPro

? VAL("9")

Y el resultado, como ya sabes, será el número 9.

Todo está muy bien, pero con CAST() podemos hacer mucho más que eso:

Listado 2. Convirtiendo de carácter a numérico con CAST()

? CAST("9" AS INTEGER)

En el Listado 2. convertimos de carácter a numérico, sería el equivalente a usar la función VAL(), pero también podríamos escribir:

Listado 3. Convirtiendo de carácter a numérico con decimales

? CAST("9" AS NUMERIC(10, 2))

Aquí, el resultado no será 9 sino que será 9.00, o sea un 9 con dos decimales.

También podrías escribir algo como:

Listado 4. Convirtiendo de carácter a numérico de punto flotante

? CAST("9.123" AS FLOAT(10,2))

Devolverá 9.12 porque se le pidieron 2 decimales. También puedes hacer a la inversa, por ejemplo:

Listado 5. Convirtiendo de numérico a carácter

? CAST(123 AS CHARACTER(10))

Convertirá el número 123 al string «123». Fíjate que es importante la cantidad de caracteres que especifiques en la función CAST(). En este ejemplo pusimos 10, siempre debe ser igual o mayor a la cantidad de dígitos para que no se trunque el resultado.

También podemos convertir un carácter a fecha, por ejemplo:

Listado 6. Convirtiendo de carácter a fecha

SET DATE BRITISH
SET CENTURY ON

? CAST("21/04/2018" AS DATE)

Y desde luego, también puedes convertir de fecha a carácter, por ejemplo:

Listado 7. Convirtiendo de fecha a carácter

? CAST(Date() AS CHARACTER(10))

Nuevamente, hay que asegurarse de que la cantidad de caracteres sea suficiente para recibir a la fecha o se obtendrá un resultado truncado.

Conclusión:

En Visual FoxPro usamos varias funciones para convertir de un tipo de datos a otro tipo de datos, en SQL solamente usamos la función CAST(), eso nos facilita recordar cual es la función que debemos usar y también nos facilita la lectura de nuestro código fuente porque es más fácil que te olvides lo que hace:

? VAL(«123456»)

a que te olvides lo que hace:

? CAST(«123» AS INTEGER)

«CAST» significa «moldear» y «AS INTEGER» se traduce: «como entero». Si lees inglés, entonces es muy fácil saber que escribiste: «moldear a 123 como entero». Y por lo tanto, entender tu código fuente es más sencillo que escribir VAL().

NOTA: En nuestros ejemplos usamos solamente algunos tipos de datos, hay más tipos de datos que puedes usar: LOGICAL, DATETIME, CURRENCY, etc. En la ayuda del Visual FoxPro los encontrarás a todos.

Artículos relacionados:

De DBF a SQL (1). Introducción

De DBF a SQL (2). Reemplazando comandos

De DBF a SQL (3). Entendiendo a los cursores

De DBF a SQL (4). Ejemplos de usar SELECT con una sola tabla

De DBF a SQL (5). Las funciones agrupadas

De DBF a SQL (6). Agrupando filas

De DBF a SQL (7). Poniéndoles condiciones a los grupos

De DBF a SQL (8). Ordenando las filas

De DBF a SQL (9). Uniendo tablas

De DBF a SQL (10). Obteniendo las primeras filas de una tabla

De DBF a SQL (11). Relacionando una tabla con otra tabla

De DBF a SQL (12). Relacionando una tabla consigo misma

De DBF a SQL (13). Relacionando a varias tablas entre sí

De DBF a SQL (14). Más sobre el uso de DISTINCT

De DBF a SQL (15). Usando subconsultas

De DBF a SQL (16). Más sobre las subconsultas

De DBF a SQL (17). Ejemplos de subconsultas

De DBF a SQL (18). Paginación

De DBF a SQL (19). Hallando los porcentajes sobre un total

De DBF a SQL (20). Los operadores especiales de comparación

De DBF a SQL (21). La técnica de los dos cursores

De DBF a SQL (22). Optimizando el uso del operador de comparación especial IN

De DBF a SQL (24). Buscando texto

De DBF a SQL (25).  Creando tablas y cursores

De DBF a SQL (26). INSERT, UPDATE, y DELETE, con subconsultas

De DBF a SQL (27). Entendiendo Cliente/Servidor

De DBF a SQL (28). Conectándose a un Servidor

De DBF a SQL (29). Las cadenas de conexión ODBC a los motores SQL más populares 

De DBF a SQL (30). Conectándose mediante ADO a las bases de datos

De DBF a SQL (31). Optimizando la escritura de los SELECT

De DBF a SQL (32). Mejorando la estética de los SELECT 

De DBF a SQL (33). Usando la función SQLEXEC()

De DBF a SQL (34). Enviándole parámetros a la función SQLEXEC()

El índice del blog VFPavanzado

 

De DBF a SQL (21). La técnica de los dos cursores

Hasta ahora, en todos los artículos de esta serie hemos visto como escribir un SELECT que nos devuelva las filas que necesitamos. Eso está muy bien y funciona perfectamente, pero tiene un problema, y es que si nuestro SELECT es muy complejo entonces se nos dificulta entender lo que hace. Para subsanar este inconveniente podemos utilizar dos (o más) comandos SELECT.

El primer SELECT hace gran parte del trabajo. El segundo SELECT completa el trabajo.

Veamos un ejemplo:

Captura 1. Enviando el SELECT a un cursor con nombre propio

En la Captura 1. el resultado del SELECT fue enviado a un cursor llamado MiCursor. Y como fue enviado a un cursor, nada vemos en la «Ventana de comandos» del Visual FoxPro. Para ver el contenido de ese cursor podemos escribir:

Captura 2. El contenido del cursor llamado MiCursor

Fíjate que lo único que escribimos es: SELECT * FROM MiCursor, tal y como te lo indica la flecha roja en la Captura 2. Lo interesante de esto es que hemos reemplazado un SELECT complejo como el mostrado en la Captura 1. por un SELECT extremadamente simple, como el que te muestra la flecha roja.

Pero además, y aquí viene lo realmente importante: podemos tratar a nuestro cursor como si de una tabla con todas esas columnas y todas esas filas se tratara. Por ejemplo, para ver todas las ventas cuyos totales sean superiores a 200 podríamos escribir:

Captura 3. Las ventas cuyos totales son mayores que 200

Desde luego que este mismo resultado podrías haber obtenido escribiendo una cláusula WHERE en la Captura 1. pero tu SELECT sería aún más complejo de lo que ya es. El SELECT de la Captura 3. es mucho más sencillo y por lo tanto mucho más fácil de entender.

Esta técnica de los dos cursores (o tres cursores, o cuatro cursores, los que necesites) nos permite obtener resultados de procesamientos muy complejos escribiendo poco y entendiendo mucho.

¿Cuándo debemos emplear la técnica de los dos cursores?

Cuando nuestro SELECT ya está muy largo y eso nos dificulta entender lo que hace. Y con mucha mayor razón si no estamos pudiendo obtener el resultado que queremos obtener. En tales casos, escribir dos (o más) cursores para dividir el problema en varias partes suele ser lo más inteligente que se puede hacer.

¿El SELECT ya está largo y te cuesta entender lo que hace y por qué no estás obteniendo el resultado que deseas obtener? Emplea la técnica de los dos cursores: divídelo en partes.

En el primer cursor pones todas las columnas que necesitarás procesar, incluyendo las subconsultas que deben estar en la lista de columnas del SELECT.

En el segundo cursor pones las cláusulas WHERE, GROUP BY, HAVING y quizás algunos JOIN que no pusiste en el primer cursor.

Si tu segundo cursor ya está largo y complejo y aún no consigues obtener el resultado que quieres, utiliza un tercer cursor. Y así sucesivamente.

Al final, el último cursor que utilices debe ser muy sencillo de entender, algo como lo que vemos en la Captura 3.

Con la técnica de los dos cursores ahorrarás mucho tiempo y quizás algún dolor de cabeza.

El defecto de esta técnica

Esta técnica no es perfecta, tiene un gran defecto.

¿Cuál es?

Que las filas se procesan más veces que las estrictamente necesarias. Por ejemplo, si la tabla tiene 1.000.000 de filas y se necesitaron 4 SELECT para obtener el resultado deseado eso significa que quizás se procesaron 4.000.000 de filas.

¿Cuál es la solución?

Una vez que se obtuvo el resultado deseado realizar el proceso inverso. Es decir lo que se agregó en el cuarto SELECT copiarlo en el tercer SELECT. Lo que se agregó en el cuarto SELECT más lo que se agregó en el tercer SELECT, agregarlo al segundo SELECT. Y lo que se agregó en el cuarto SELECT, en el tercer SELECT, y en el segundo SELECT, agregarlo al primer SELECT.

Se tendrá así un SELECT grande, feo, muy complejo, pero que funcionará perfectamente y que además será muy rápido porque procesará a todas esas filas una sola vez.

Conclusión:

Cuando el SELECT ya se está tornando muy complejo, es difícil de entender lo que hace, y no se está consiguiendo obtener el resultado deseado, lo más inteligente que se puede hacer es dividir el problema en partes.

O sea, no usar un solo SELECT sino dos o más SELECT.

Para ello, lo usual es que en el primer SELECT se escriban todas las columnas que se necesitan, esto suele implicar a la cláusula JOIN para traer a las columnas de otras tablas. El cursor así obtenido se utiliza en un segundo SELECT y es en este segundo SELECT donde se escriben las cláusulas WHERE, GROUP BY, y HAVING.

Si este segundo SELECT también se está tornando muy complejo y aún no se está consiguiendo obtener el resultado deseado, entonces se crea un cursor con nombre propio que será utilizado por un tercer SELECT. Y así sucesivamente.

El último SELECT que se escriba debe ser muy simple, muy sencillo, muy fácil de entender. Desde luego que eso de «muy fácil de entender» depende de cada quien, porque un SELECT que para una persona resulta «muy fácil de entender» para otra persona puede ser muy difícil de entender. Lo importante es que sea muy fácil de entender para la persona que escribe el SELECT.

Empleando la técnica de los dos cursores se puede muy rápidamente resolver problemas muy complicados. Trata de utilizarla en cada ocasión que se te dificulte obtener el resultado deseado.

Artículos relacionados:

De DBF a SQL (1). Introducción

De DBF a SQL (2). Reemplazando comandos

De DBF a SQL (3). Entendiendo a los cursores

De DBF a SQL (4). Ejemplos de usar SELECT con una sola tabla

De DBF a SQL (5). Las funciones agrupadas

De DBF a SQL (6). Agrupando filas

De DBF a SQL (7). Poniéndoles condiciones a los grupos

De DBF a SQL (8). Ordenando las filas

De DBF a SQL (9). Uniendo tablas

De DBF a SQL (10). Obteniendo las primeras filas de una tabla

De DBF a SQL (11). Relacionando una tabla con otra tabla

De DBF a SQL (12). Relacionando una tabla consigo misma

De DBF a SQL (13). Relacionando a varias tablas entre sí

De DBF a SQL (14). Más sobre el uso de DISTINCT

De DBF a SQL (15). Usando subconsultas

De DBF a SQL (16). Más sobre las subconsultas

De DBF a SQL (17). Ejemplos de subconsultas

De DBF a SQL (18). Paginación

De DBF a SQL (19). Hallando los porcentajes sobre un total

De DBF a SQL (20). Los operadores especiales de comparación

De DBF a SQL (22). Optimizando el uso del operador de comparación especial IN

De DBF a SQL (23). Usando la función CAST()

De DBF a SQL (24). Buscando texto

De DBF a SQL (25).  Creando tablas y cursores

De DBF a SQL (26). INSERT, UPDATE, y DELETE, con subconsultas

De DBF a SQL (27). Entendiendo Cliente/Servidor

De DBF a SQL (28). Conectándose a un Servidor

De DBF a SQL (29). Las cadenas de conexión ODBC a los motores SQL más populares 

De DBF a SQL (30). Conectándose mediante ADO a las bases de datos

De DBF a SQL (31). Optimizando la escritura de los SELECT

De DBF a SQL (32). Mejorando la estética de los SELECT 

De DBF a SQL (33). Usando la función SQLEXEC()

De DBF a SQL (34). Enviándole parámetros a la función SQLEXEC()

El índice del blog VFPavanzado

 

De DBF a SQL (20). Los operadores especiales de comparación

Como ya sabes, para filtrar las filas que aparecerán en el resultado de un SELECT se usan las cláusulas WHERE y HAVING.

En ellas puedes usar los operadores de comparación tradicionales: =, <>, >, >=, <, <=

Pero además, si en la cláusula WHERE has escrito una subconsulta, puedes usar allí unos operadores de comparación especiales. Ellos son:

  • ALL. Todas las filas obtenidas de la subconsulta deben cumplir con la comparación (=, <>, >, >=, <, <=)
  • ANY. Cualquiera de las filas de las obtenidas de la subconsulta debe cumplir con la comparación (=, <>, >, >=, <, <=)
  • EXISTS. La subconsulta devolvió al menos una fila. Muchas veces se lo usa con el operador lógico NOT
  • IN. En alguna de las filas de la subconsulta debe encontrarse el valor buscado. Muchas veces se lo usa con el operador lógico NOT. Es el operador de comparación especial más ampliamente utilizado
  • SOME. Es idéntico a ANY, funciona exactamente igual

Los operadores especiales de comparación: ALL, ANY, SOME, son raramente utilizados, muy pocas veces los verás en los SELECT. El que más se usa es IN frecuentemente con su negativo, es decir: NOT IN. También se usa bastante EXISTS y muchas veces con su negativo, es decir: NOT EXISTS.

En general (no siempre, pero muchísimas veces sí) se puede cambiar una comparación escrita con IN por una comparación escrita con EXISTS y se obtiene exactamente el mismo resultado, no hay diferencia. ¿Cuál es preferible utilizar entonces, IN o EXISTS? Depende de tu gusto y de las circunstancias, aunque EXISTS suele ser un poco más rápido.

La sintaxis para usar los operadores de comparación especiales: ALL, ANY, SOME es la siguiente:

MiColumna [operador_de_comparacion] [operador_de_comparacion_especial] MiSubconsulta

Por ejemplo:

MiColumna > ALL (SELECT ColumnaSubconsulta FROM MiOtraTabla)

La subconsulta devolverá varias filas que contendrán un valor y MiColumna deberá ser mayor que todos esos valores (porque en este ejemplo usamos el operador de comparación mayor que).

NOTA: Habíamos dicho en artículos anteriores que las subconsultas deben devolver solamente un valor, o sea una sola fila y una sola columna. Eso no se aplica cuando se utilizan los operadores especiales de comparación, ya que en este caso las subconsultas pueden (y frecuentemente lo hacen) devolver varias filas.

¿Cuándo se pueden usar los operadores especiales de comparación?

Cuando en la pregunta que se debe responder se encuentran las palabras: todos, ninguno, alguno, siempre, nunca, a veces, al menos una vez.

Ejemplos:

  • ¿Quiénes son los alumnos que aprobaron todos los exámenes?
  • ¿Quiénes son los alumnos que se aplazaron en al menos un examen?
  • ¿Quiénes son los alumnos que nunca faltaron a clases?
  • ¿Cuáles son los productos que se venden todos los días?
  • ¿Cuáles son los productos que durante el mes pasado nunca se vendieron?
  • ¿Cuáles son los productos que se vendieron al menos una vez durante la semana pasada?
  • ¿Quiénes son los clientes que siempre abonaron sus cuotas en las fechas fijadas?
  • ¿Quiénes son los clientes que nunca abonaron sus cuotas en las fechas fijadas?
  • ¿Quiénes son los clientes que al menos una vez se atrasaron con sus cuotas?
  • ¿Quiénes son los proveedores que nos proveen el producto XXX?
  • ¿Quiénes son los proveedores que nos proveen el producto XXX a un precio menor que 80 dólares?

Estos son solamente algunos pocos ejemplos de lo que se puede responder cuando se usan los operadores de comparación especiales. Hay que aclarar que todas estas preguntas también se podrían responder sin utilizarlos, pero en ese caso se escribiría más y probablemente el código no sea tan fácil de entender. Por algo es que existen estos operadores ¿verdad?

Ejemplos de uso:

Para entender mejor todo esto veremos algunos ejemplos.

Captura 1. Las filas de la tabla PRODUCTOS

Captura 2. Las filas de la tabla VENTASDET

Captura 3. Las filas de la tabla VENTASCAB

Ejemplo 1. ¿Cuáles productos se vendieron con un precio de venta igual a 12?

Los precios de venta de los productos pueden variar, el precio de venta que tienen hoy puede ser distinto al precio de venta que tenían hace unos meses. Si nos interesan los productos que alguna vez se vendieron con un precio de venta igual a 12, entonces …

Captura 4. Los productos que alguna vez se vendieron con un precio unitario de 12

El operador de comparación especial ANY indica que el precio de venta 12 puede encontrarse en cualquier fila de la tabla VENTASDET.

Ejemplo 2. ¿Cuáles productos alguna vez se vendieron con una cantidad mayor o igual que 10?

Captura 5. Todos los productos cuya cantidad vendida fue alguna vez mayor o igual que 10

Todos los productos que se encuentran en la Captura 5. en al menos una venta su cantidad fue mayor o igual que 10.

Ejemplo 3. ¿Cuáles son las Facturas en las cuales se vendieron uvas?

Captura 6. Todas las Facturas en las cuales se vendió uvas

Aquí usamos la función EXISTS() para hallar todas las Facturas de ventas en las cuales se hayan vendido uvas.

Ejemplo 4. ¿Cuáles son las Facturas en las cuales no se vendieron ciruelas?

Captura 7. Todas las Facturas en las cuales no se vendieron ciruelas

En la Captura 7. la condición es negativa. O sea, se escribió: NOT EXISTS. Aquí buscamos las Facturas en las cuales no existen ciruelas.

Comentario:

Desde luego que hay muchísimos usos más que se les puede dar a los operadores de comparación especiales, estos fueron unos sencillos ejemplos para que te vayas haciendo una idea de las posibilidades que dispones. En sucesivos artículos de esta serie irás viendo otros ejemplos.

Conclusión:

Cuando necesitamos filtrar a nuestra tabla según condiciones en las cuales se encuentren las palabras: todos, ninguno, alguno, siempre, nunca, a veces, podemos usar los llamados operadores de comparación especiales.

Sus nombres son: ALL, ANY, EXISTS, IN, SOME y se usan en la cláusula WHERE.

El más usado es IN y luego EXISTS. Los demás, aunque pueden ser muy útiles, son raramente utilizados, quizás por falta de costumbre de los programadores.

Artículos relacionados:

De DBF a SQL (1). Introducción

De DBF a SQL (2). Reemplazando comandos

De DBF a SQL (3). Entendiendo a los cursores

De DBF a SQL (4). Ejemplos de usar SELECT con una sola tabla

De DBF a SQL (5). Las funciones agrupadas

De DBF a SQL (6). Agrupando filas

De DBF a SQL (7). Poniéndoles condiciones a los grupos

De DBF a SQL (8). Ordenando las filas

De DBF a SQL (9). Uniendo tablas

De DBF a SQL (10). Obteniendo las primeras filas de una tabla

De DBF a SQL (11). Relacionando una tabla con otra tabla

De DBF a SQL (12). Relacionando una tabla consigo misma

De DBF a SQL (13). Relacionando a varias tablas entre sí

De DBF a SQL (14). Más sobre el uso de DISTINCT

De DBF a SQL (15). Usando subconsultas

De DBF a SQL (16). Más sobre las subconsultas

De DBF a SQL (17). Ejemplos de subconsultas

De DBF a SQL (18). Paginación

De DBF a SQL (19). Hallando los porcentajes sobre un total

De DBF a SQL (21). La técnica de los dos cursores

De DBF a SQL (22). Optimizando el uso del operador de comparación especial IN

De DBF a SQL (23). Usando la función CAST()

De DBF a SQL (24). Buscando texto

De DBF a SQL (25).  Creando tablas y cursores

De DBF a SQL (26). INSERT, UPDATE, y DELETE, con subconsultas

De DBF a SQL (27). Entendiendo Cliente/Servidor

De DBF a SQL (28). Conectándose a un Servidor

De DBF a SQL (29). Las cadenas de conexión ODBC a los motores SQL más populares 

De DBF a SQL (30). Conectándose mediante ADO a las bases de datos

De DBF a SQL (31). Optimizando la escritura de los SELECT

De DBF a SQL (32). Mejorando la estética de los SELECT 

De DBF a SQL (33). Usando la función SQLEXEC()

De DBF a SQL (34). Enviándole parámetros a la función SQLEXEC()

El índice del blog VFPavanzado