14 de marzo de 2003

SQL Select en VFP y T-SQL

Artículo original: SQL SELECT in VFP and T SQL
http://www.ml-consult.co.uk/foxst-38.htm
Autor: Mike Lewis
Traducido por: Ana María Bisbé York

Veamos estas diferencias en la sintaxis cuando se mueven datos entre Visual FoxPro y SQL Server.

A lo largo de estos últimos años, el Fox team en Microsoft ha hecho un trabajo excelente para acercar lo más posible el sublenguaje SQL de VFP para que acepten los estándares. En particular, se ha hecho el comando SELECT más cercano, conforme a los estándares ANSI-92, lo que es una buena noticia para los desarrolladores que necesitan moverse entre VFP y tener datos en Bases de datos SQL Server y Oracle.

Aun la versión actual de SELECT tiene suficientes diferencias que atrapan al programador desprevenido. Algunas de estas diferencias reflejan peculiaridades del lenguaje FoxPro, mientras otras son servicios tan genuinamente útiles - aunque no son estándar - que los desarrolladores no dejarían de emplearlo - por ejemplo, la capacidad para enviar los resultados de una consulta directamente a una matriz.

En este artículo, veremos las diferencias entre la sintaxis de las versiones de SELECT para VFP y SQL Server (T-SQL). Nosotros compilamos la lista específicamente teniendo en mente SQL Server 2000 , casi todos los puntos se refieren a las versiones 7 y 2005 (hemos actualizado el artículo después de que ha salido SQL Server 2005). Si conoce otras diferencias no cubiertas aquí, estaremos encantados de recibir su comentario.

Orden de las cláusulas

VFP siempre ha estado relajado en cuanto al orden de las cláusulas en una instrucción y esto se aplica al SELECT. Coloque su ORDER BY antes de la cláusula FROM, o su TOP N justo al final del comando y VFP ni siquiera va a pestañar. T-SQL es mucho más estricto. En general T-SQL exige que la cláusula aparezca en orden en el que se ha escrito en el diagrama de sintaxis del archivo de Ayuda.

Nombres de columnas vacíos

Algo raro de T-SQL que sorprende a los desarrolladores VFP, es la posibilidad de generar consultas de resultados con columnas de nombres vacías. Esto ocurre de forma predeterminada con columnas calculadas o agregadas. Por ejemplo, en los resultados de esta consulta:
SELECT EmployeeID, UPPER(LEFT(LastName,4))
FROM Employees
La segunda columna no tendrá nombre, en VFP, el nombre de la columna va a ser de forma predeterminada como Exp_1. En ambas versiones, puede emplear la cláusula AS para asignar un nombre a una columna:
SELECT EmployeeID,
UPPER(LEFT(LastName,4)) AS ShortName 
FROM Employees
pero T-SQL brinda una sintaxis alternativa que no está soportada por VFP.
SELECT EmployeeID, 
ShortName = UPPER(LEFT(LastName,4)) 
FROM Employees

WITH TIES

En T-SQL puede agregar WITH TIES es la cláusula TOP N para indicar que el conjunto resultante debe incluir y agrupar los valores en la fila enésima. Entonces, si especifica TOP 10 WITH TIES, y las filas 10ma y 11na tienen el mismo valor. Obtendrá 11 filas en el conjunto resultante. VFP no admite esta cláusula.

Cláusula FROM opcional

Aquí hay otra diferencia que sorprende a mucha gente. En T-SQL, es legal omitir la cláusula FROM en un comando SELECT - pero sólo si las expresiones, en la lista de expresiones, no referencian a ninguna columna en una tabla. Haciendo esto siempre genera un conjunto resultante con una fila exactamente. El comando siguiente, por tanto, va a producir un conjunto resultante que contiene solamente la fecha-hora actual:
SELECT GETDATE() AS Current
VFP va a oponerse resueltamente a un comando de este tipo.

Operador LIKE

Tanto VFP y T-SQL admiten caracteres comodín ('_' y '%') con el operador LIKE. Esto representa un único carácter y un número arbitrario de caracteres respectivamente. Por tanto, en esta consulta:
SELECT * FROM Products 
WHERE Product_Name LIKE '%chocolate%'
Tendremos todos los productos cuyo nombre incluya "chocolate"


T-SQL lo hace un poco mejor, ya que nos permite utilizar corchetes para denotar rangos y conjuntos. Por ejemplo, la sentencia siguiente va a encontrar productos cuyo código comience con A,B,C seguido por dos dígitos:
SELECT * FROM Products 
WHERE Product_Code LIKE '[A-C][0-9][0-9]'
Desafortunadamente, esta característica tan cómoda, no está disponible en VFP.

Las funciones agregadas y GROUP BY

Esto ha atrapado a muchos desarrolladores VFP, especialmente desde VFP 8.0, que fue donde apareció por primera vez. En versiones anteriores, la sintaxis siguiente estaba admitida:
SELECT Country, City, AVG(Inv_Total) 
FROM Invoices GROUP BY Country
Sin embargo, aunque es legal, la sintaxis provocará datos potencialmente incorrectos. En el ejemplo de arriba, VFP va a seleccionar una ciudad arbitraria para que se corresponda con cada país, incluso aunque ese país pueda tener muchas ciudades. SQL ANSI-92 impone una regla muy sencilla: si la consulta tiene cláusula GROUP BY. T-SQL fuerza estrictamente esa regla, como hace VFP 8.0 y superior, pero las versiones anteriores de FoxPro no (en VFP 8.0 y superior, puede sobreescribir este comportamiento estableciendo ENGINEBEHAVIOR 70).

Agrupar columnas numéricas

Todavía en la cláusula GROUP BY, la siguiente sintaxis es legal en todas las versiones de VFP:
SELECT Country, AVG(Inv_Total) 
FROM Invoices GROUP BY 1
T-SQL puede ahogarse aquí, porque las columnas numéricas no están admitidas en la cláusula GROUP BY. Tendría que utilizar la forma siguiente (lo que también es legal en VFP):
SELECT Country, AVG(Inv_Total) 
FROM Invoices GROUP BY Country
Sin embargo, ambas versiones permiten utilizar columnas numéricas con ORDER BY.

Cláusula HAVING

En VFP, puede utilizar la cláusula HAVING, incluso si no existe cláusula GROUP BY. En este caso, HAVING se comporta como WHERE. En T-SQL, HAVING no está permitido sin GROUP BY.

Outer joins

En T-SQL, no puede especificar un join único como OUTER. A diferencia en VFP, puede incluir la palabra apropiada: LEFT, RIGHT o FULL.

Proyecciones y tablas derivadas

Tanto VFP y T-SQL admiten subconsultas en la cláusula WHERE. Tanto en VFP 9.0, como en T-SQL, las subconsultas pueden aparecer además, en la lista de expresiones y en las cláusulas FROM y JOIN. Las subconsultas en la lista de expresiones se llaman proyecciones, mientras aquellas de FROM o JOIN se llaman tablas derivadas. El archivo Ayuda tanto para VFP 9.0 como para SQL Server tienen ejemplos de cómo se pueden emplear.

VFP 8.0 y anterior no admiten proyecciones o tablas derivadas.

Destino de las consultas

VFP permite enviar los resultados de las consultas a varios destinos. De forma predeterminada, van a un cursor que crea VFP y muestra con un Browse. Puede utilizar la cláusula PREFERENCE para especificar opciones para esta ventana. Alternativamente, puede utilizar la cláusula TO para enviar los resultados de la pantalla, una impresora o un archivo texto. O puede utilizar INTO para localizar los resultados en un cursor, tabla o matriz.

En T-SQL, los resultados de las consultas se devuelven sencillamente al proceso del cliente en el formulario o el conjunto resultante. La única opción para redireccionarlos es para enviarlos a una tabla nueva, lo que puede hacer de esta forma:
SELECT * INTO Choc_Products FROM Products 
WHERE Product_Name LIKE '%chocolate%'
A diferencia de VFP, la posición de la cláusula INTO es significativa - puede aparecer entre la lista de expresiones y la cláusula FROM - no se puede incluir la palabra clave TABLE.
Si desea enviar el resultado de la consulta para una tabla temporal - similar a un cursor en VFP - utilice la sintaxis de nombre como antes; pero inicie el nombre de la tabla con #. Las tablas, cuyos nombres comiencen con #, se guardan en la base de datos Tempdb de SQL Server, y se destruyen cuando las conecciones que la crean, se cierran.

Los campos memo y general en cláusulas DISTINCT

VFP 7.0 y superior admite especificar campos memo y general en la lista de expresiones de una consulta que incluye la palabra clave DISTINCT, aunque esas consultas producen resultados incorrectos. Para VFP 8.0, el  MS FoxTeam ha mejorado esta sintaxis. Ahora se prohíben  los campos memo y general en consultas DISTINCT, a menos que ENGINEBEHAVIOR sea 70.

Los equivalentes de campos memo y general en SQL Server son columnas de texto e imagen. T-SQL no admite columnas de esos tipos en el conjunto resultante de consultas que tienen una cláusula DISTINCT.

Cantidad de tablas

VFP 8.0 y anteriores impone un límite de 30 tablas (para ser preciso, 30 alias) en una sentencia SELECT. La equivalencia en T-SQL es 256. VFP 9.0 teóricamente, no tiene límites .

Enlazando subconsultas

En VFP 8.0 y anteriores sólo puede tener un único nivel de subconsultas enlazadas. En VFP 9.0, teóricamente no existen límites. En T-SQL, el límite es 32 niveles.

Valores nulos

Si desea excluir valores nulos desde un conjunto resultante (o excluir todo excepto valores nulos), puede (y debe) utilizar

IS [NOT] NULL en la cláusula WHERE. Pero aplica en ambos casos VFP y T-SQL.

Sea cuidadoso con la función ISNULL() que es aparentemente similar. En VFP, esta función hace lo que sugiere su nombre: le dice si la evaluación de la expresión dada es null. En T-SQL, ISNULL() es más parecido a la función NVL() de VFP . Tiene dos argumentos: si el primer argumento es nulo, la función devuelve el segundo argumento, en caso contrario devuelve el primero.

Mike Lewis Consultants Ltd. (Enero 2006).

2 comentarios :

  1. QUE TAL AMIGOS SOLICITANDO DE SU VALIOSO APOYO, TENGO UN DBF CON 40000 REGISTROS, ESTA TABLA CONTIENE ESTADOS DE CUENTA DE PERSONAS POR FECHA DE PAGO, POR LO CUAL VIENE MUCHOS REGISTROS POR PERSONA.

    NECESITO SELECCIONAR LOS PRIMEROS 5 REGISTROS DE CADA UNA.. COMO PUEDO HACERLO??? DESDE YA GRACIAS.

    POR FAVOR AYUDAR......

    ResponderBorrar
  2. Tarde en responder. Buscando solución a otro problema me encuentro tu consulta. Probablemente ya la resolviste, pero como todavía está reciente quiero hacer el ejercicio mental para ver si te sirve:
    La única solución que veo sería hacer un desarrollo con la siguiente lógica:
    - Copia la información de tu base a una base temporal (MiBaseTemporal)
    - Haces un Scan a MiBaseTemporal. y dejas solo los cinco primeros registros por cada persona. Algo así, aclarando que la base tiene que estar ordenada por la identificación de la persona.

    select MiBase
    copy to MiBaseTemporal
    select MiBaseTemporal
    scan
    CuentaPersonas=1
    VarIdenPersona=MiBaseTemporal.IdPersona &&variable para identificacion de la persona
    do WHILE VarIdenPersona=MiBaseTemporal.IdPersona &&mientras la persona sea igual
    IF CuentaPersonas<=5
    DELETE
    CuentaPersonas=CuentaPersonas+1
    ENDIF
    SKIP &&AVANZO AL SIGUIENTE REGISTRO
    ENDDO
    SKIP -1 &&CON ESTO GARANTIZO QUE EL APUNTADOR DE LA BASE VUELVA AL REGISTRO ANTERIOR AL CAMBIO DE PERSONA E INICIE EN LA NUEVA PERSONA
    ENDSCAN

    PACK &&BORRO FISICAMENTE LOS REGISTROS

    - Luego hago el select sobre la base MiBaseTemporal.
    - por último elimino esta base para no ocupar espacio en disco

    Ya lo probé y funciona si la base está ordenada.

    ResponderBorrar

Los comentarios son moderados, por lo que pueden demorar varias horas para su publicación.