martes, 16 de febrero de 2016

Fundamentos de Consultas T-SQL

Transact-SQL (T-SQL) es el lenguaje principal utilizado para gestionar y manipular datos en Microsoft SQL Server. Este post describe las raíces de este lenguaje, la terminología y la mentalidad que necesita adoptar cuando escribe código T-SQL.

Entendiendo los Fundamentos de T-SQL

Muchos aspectos de computación, como los lenguajes de programación, evolucionan basados en la intuición y la tendencia actual. Sin bases sólidas, su vida útil podría ser muy corta, y si sobreviven, a menudo los cambios son muy rápidos, debido a los cambios en las tendencias. T-SQL es diferente, principalmente porque tiene sólidos fundamentos, matemáticos. No necesita ser un matemático para escribir buen código SQL, pero siempre y cuando entienda cuales son los fundamentos, y algunos de sus principios claves, entenderá mejor de lo que trata el lenguaje. Sin estos fundamentos, todavía se puede escribir código T-SQL, incluso código que se ejecute exitosamente, pero sería como tomar la sopa con un tenedor!

Evolución de T-SQL

Como se ha mencionado, a diferencia de muchos otros aspectos de la computación, el T-SQL está basado en sólidos fundamentos matemáticos. Comprender algunos de los principios claves de estos fundamentos, puede ayudar a entender mejor el lenguaje con el que está tratando. Entonces pensará en términos de T-SQL al codificar en T-SQL; en lugar de codificar con T-SQL, mientras piensa en términos de procedimientos.
La Figura siguiente ilustra la evolución de T-SQL de sus fundamentos matemáticos principales.

T-SQL es el lenguaje principal utilizado para gestionar y manipular datos en el Sistema de Gestión de Base de Datos Relacional (RDBMS) principal, de SQL Server; ya sea en las instalaciones o en la nube (Microsoft Windows Azure SQL Database). SQL Server también soporta otros lenguajes, como Microsoft Visual C# y Microsoft Visual Basic, pero T-SQL es usualmente el lenguaje preferido para la gestión y la manipulación de datos.
T-SQL es un dialecto del SQL estándar. SQL es un estándar tanto de la Organización Internacional de Estandarización (ISO) y del Instituto de Estándares Nacionales Americano (ANSI). Los dos estándares para SQL son básicamente lo mismo. El estándar SQL sigue evolucionando con el tiempo. A continuación se presenta una lista de las principales revisiones del estándar hasta el momento:
·         SQL-86
·         SQL-89
·         SQL-92
·         SQL:1999
·         SQL:2003
·         SQL:2006
·         SQL:2008
·         SQL:2011
Todos los vendors líderes de bases de datos, incluyendo Microsoft, implementan un dialecto de SQL como el principal lenguaje para gestionar y manipular datos en sus plataformas de bases de datos. Por lo tanto, los elementos de lenguaje principales son similares. Sin embargo, cada vendor decide qué característica implementar y cual no. Además, el estándar algunas veces deja algunos aspectos, como una opción de implementación. Usualmente cada vendor también implementa extensiones al estándar, en los casos en donde el vendor sienta que una característica importante no está cubierta por el estándar.
Escribir en una forma estándar es considerada una buena práctica. Cuando así lo hace, el código es más portable. Su conocimiento es más portable, también, porque le resulta más fácil comenzar a trabajar con nuevas plataformas. Cuando el dialecto que está trabajando, soporta tanto una forma estándar y no estándar de hacer algo, siempre debe preferir la forma estándar, como su elección predeterminada. Debe considerar una opción no estándar sólo cuando obtiene beneficios importantes, y que no están previstos por la alternativa estándar.
Como un ejemplo de cuándo debe elegir la forma estándar, T-SQL soporta dos operadores "no es igual a": <> y =!. El primero es estándar y el último no lo es. Este caso es pan comido: ¡elige el estándar!
Como un ejemplo de cuando la elección del estándar o del no estándar dependen de las circunstancias, considere lo siguiente: T-SQL soporta varias funciones que convierten un valor de origen a un tipo de destino. Entre ellos se encuentran las funciones CAST y CONVERT. El primero es estándar y el último no lo es. La función CONVERT no estándar tiene un argumento de estilo que CAST no soporta. Debido a que CAST es estándar, debería considerarlo como su elección por defecto, para conversiones. Debería considerar el uso de CONVERT sólo cuando necesita confiar en el argumento de estilo.
Otro ejemplo de la elección de la forma estándar, está en la terminación de las sentencias T-SQL. De acuerdo al SQL estándar, debe terminar sus sentencias con un punto y coma. Actualmente en T-SQL no es un requisito para todas las sentencias, sólo en los casos donde de otro modo, se presenta ambigüedad de elementos de código, tales como en la cláusula WITH de una Expresión de Tabla Común (CTE). Aún debería seguir el estándar y terminar todas sus sentencias, incluso cuando en la actualidad no es requerida.
El SQL estándar está basado en el modelo relacional, el cual es un modelo matemático para la gestión y la manipulación de datos. El modelo relacional fue inicialmente creado y propuesto por Edgar F. Codd en 1969. Desde entonces, ha sido explicado y desarrollado por Chris Date, Hugh Darwen, y otros.
Un concepto erróneo común es que el nombre "relacional" tiene que ver con las relaciones entre las tablas (es decir, las llaves foráneas). Actualmente, el origen verdadero para el nombre del modelo es el concepto matemático de relación.
Una relación en el modelo relacional es lo que SQL llama una tabla. Los dos no son sinónimos. Se podría decir que una tabla es un intento por parte de SQL para representar una relación (en adición a una variable de relación, pero no necesariamente obtiene eso aquí). Algunos podrían decir que no es un intento muy exitoso. Aunque el SQL está basado en el modelo relacional, se desvía de este, en muchas formas. Pero es importante notar que así como entienda los principios del modelo, podrá utilizar SQL, o más precisamente, el dialecto que esté utilizando, en una forma relacional.
Regresando a una relación, el cual es lo que SQL intenta representar con una tabla: una relación tiene una cabecera y un cuerpo. La cabecera es un conjunto de atributos (que SQL intenta representar con columnas), cada uno de un tipo dado. Un atributo es identificado por el nombre y el tipo del nombre. El cuerpo es un conjunto de tuplas (qué SQL intenta representar con filas). Cada cabecera de tupla es la cabecera de la relación. Cada valor de cada atributo de tupla, es de su tipo respectivo.
Algunos de los principios más importantes a entender acerca de T-SQL, parte de los fundamentos principales del modelo relacional, la teoría de conjuntos y la lógica de predicado.
Recuerde que la cabecera de una relación es un conjunto de atributos, y el cuerpo un conjunto de tuplas. Por tanto, ¿Qué es un conjunto? De acuerdo con el creador de la teoría matemática de conjuntos, Georg Cantor, un conjunto es descrito de la siguiente manera:
Por un "conjunto" nos referimos a cualquier colección M en un todo definido, de objetos distintos m (que son llamados los "elementos" de M) de nuestra percepción o de nuestro pensamiento.
—George Cantor, en “Georg Cantor” por Joseph W. Dauben
         (Princeton University Press, 1990)
Hay un número de principios muy importantes en esta definición que, si lo entendió, debería tener una repercusión directa en sus prácticas de codificación de T-SQL. Por un lado, note el término todo. Un conjunto debe ser considerado como un todo. Esto significa que no interactúa con los elementos individuales del conjunto, sino con el conjunto como un todo.
Note el término distinto, un conjunto no tiene duplicados. Codd comentó una vez sobre los aspectos de no duplicados: "Si algo es cierto, entonces decirlo dos veces no lo hará más cierto". Por ejemplo, el conjunto {a, b​​, c} es considerado igual al conjunto {a, a, b, c, c, c}.
Otro aspecto crítico de un conjunto no aparece explícitamente en la definición antes mencionada por Cantor, sino más bien está implícito, es que no hay relevancia en el orden de los elementos de un conjunto. Por el contrario, una secuencia (que es un conjunto ordenado), por ejemplo, tiene un orden de sus elementos. Combinando los aspectos de no duplicado y de no relevancia del orden, significa que el conjunto {a, b​​, c} es igual al conjunto {b, a ,c ,c ,a, c} .
La otra rama de las matemáticas del que está basado el modelo relacional, es llamada la lógica de predicado. Un predicado es una expresión que cuando se le atribuye a algún objeto, hace que una preposición sea Verdadera o Falsa. Por ejemplo, "salario mayor a S/. 5,000" es un predicado. Puede evaluar este predicado para un empleado específico, en cuyo caso se tiene una preposición. Por ejemplo, suponga que para un empleado en particular, su salario es de S/. 6,000. Cuando evalúa la preposición para ese empleado, se obtiene una preposición Verdadera. En otras palabras, un predicado es una preposición parametrizada.
El modelo relacional utiliza predicados como uno de sus elementos centrales. Puede forzar la integridad de datos por utilizar predicados. Puede filtrar los datos por utilizar predicados. Puede incluso utilizar los predicados para definir el modelo de datos. En primer lugar, identifica las preposiciones que necesita que sean almacenadas en la base de datos. He aquí una preposición de ejemplo: un pedido con IDPedido de 10248, fue colocado el 12 de febrero del 2012, por el cliente con IDCliente de 7, y manejado por el empleado con IDEmpleado de 3. Entonces, crea predicados de las preposiciones para eliminación de datos y mantenimiento de la cabecera. Recuerde, la cabecera es un conjunto de atributos, cada uno identificado por el nombre y el nombre del tipo.

Usando T-SQL en una Manera Relacional

Como se mencionó, T-SQL está basada en SQL, que a su vez es basada en el modelo relacional. Sin embargo, hay un número de maneras en la cual SQL; y por lo tanto, T-SQL, se desvía del modelo relacional. Pero el lenguaje le da las herramientas suficientes para que, si comprende el modelo relacional, pueda utilizar el lenguaje de una manera relacional, y por lo tanto escribir código más eficiente.
Recuerde que una relación tiene una cabecera y un cuerpo. La cabecera es un conjunto de atributos y el cuerpo es un conjunto de tuplas. Recuerde de la definición de conjunto, que un conjunto es supuestamente considerado como un todo. Lo que traducido a T-SQL, es que supuestamente escribe consultas que interactúan con las tablas como un todo. Debe tratar de evitar usar construcciones iterativas como cursores y bucles que iteran a través de las filas de una en una. También debería tratar de evitar pensar en términos iterativos, porque este tipo de pensamiento es lo que conduce a las soluciones iterativas.
Para las personas con antecedentes de programación por procedimientos, la forma natural de interactuar con los datos (en un archivo, recordset, o datareader) es con iteraciones. Así que usar los cursores y otras construcciones iterativas en T-SQL es, en cierto modo, una extensión de lo que ya conocen. Sin embargo, la forma correcta desde la perspectiva del modelo relacional, es no interactuar con las filas de una en una; más bien, utilizar las operaciones relacionales y retornar un resultado relacional. Esto, en T-SQL, se traduce a escribir consultas.
Recuerde también que un conjunto no tiene duplicados. En T-SQL no siempre se impone esta regla. Por ejemplo, puede crear una tabla sin una llave. En tal caso, se le ha permitido tener filas duplicadas en la tabla. Para seguir la teoría relacional, necesita imponer la unicidad en sus tablas; por ejemplo, por utilizar una llave primaria o una restricción unique.
Aun cuando la tabla no permite las filas duplicadas, una consulta sobre la tabla puede aún retornar filas duplicadas en su resultado, aquí hay un ejemplo para fines de ilustración. Examine la siguiente consulta.
USE TSQL2012;
SELECT country
FROM HR.Employees;
La consulta es aplicada sobre la base de datos de ejemplo TSQL2012 (http://1drv.ms/1KoMdxI). Retorna el atributo country de los empleados almacenado en la tabla HR.Employees. De acuerdo al modelo relacional, una operación relacional sobre una relación supuestamente retornará una relación. En este caso, esto debería traducirse para retornar el conjunto de países donde hay empleados, con énfasis en conjuntos, como en no duplicados. Sin embargo, T-SQL no intenta eliminar los duplicados por defecto.
Aquí está la salida de esta consulta.
Country
---------------
USA
USA
USA
USA
UK
UK
UK
USA
UK
De hecho, T-SQL es basado más en la teoría de multiconjuntos que en la teoría de conjuntos. Un multiconjunto (también conocido como un bag o un superconjunto) en muchos aspectos es similar a un conjunto, pero puede tener duplicados. Como se ha mencionado,  el lenguaje T-SQL le da las herramientas suficientes para que si quiere seguir la teoría relacional, puede hacerlo. Por ejemplo, el lenguaje le proporciona una cláusula DISTINCT para eliminar los duplicados. Aquí está la consulta revisada.
SELECT DISTINCT country
FROM HR.Employees;
Aquí está la salida de la consulta revisada.
Country
---------------
UK
USA
Otro aspecto fundamental de un conjunto, es que no tiene relevancia el orden de los elementos. Por esta razón, las filas en una tabla no tienen algún orden en particular, conceptualmente. Así que cuando emite una consulta sobre una tabla y no indica explícitamente en qué orden de presentación en particular, quiere retornar las filas, el resultado se supone que sea relacional. Por lo tanto, no debe asumir algún orden específico para las filas en el resultado, sin importar lo que sabe acerca de la representación física de los datos, por ejemplo, cuando los datos son indexados.
Como ejemplo, considere la siguiente consulta.
SELECT empid, lastname
FROM HR.Employees;
Cuando esta consulta fue ejecutada en un sistema, retorna la siguiente salida, que luce como si fuera ordenada por la columna lastname.
empid lastname
------ -------------
5      Buck
8      Cameron
1      Davis
9      Dolgopyatova
2      Funk
7      King
3      Lew
4      Peled
6      Suurs
Incluso si las filas fueran retornadas en un orden diferente, el resultado aún tendría que ser considerado correcto. SQL Server puede elegir entre diferentes métodos de acceso físico para procesar la consulta, conociendo que no necesita garantizar el orden en el resultado. Por ejemplo, SQL Server podría decidir paralelizar la consulta o explorar los datos en el orden de archivo (en lugar del orden de índice).
Si necesita garantizar un orden de presentación específico a las filas en el resultado, necesita agregar una cláusula ORDER BY a la consulta, de la siguiente manera.
SELECT empid, lastname
FROM HR.Employees
ORDER BY empid;
Esta vez, el resultado no es relacional, es lo que el SQL estándar llama un cursor. El orden de la filas en la salida está garantizada, basada en el atributo empid. Aquí está la salida de esta consulta.
empid lastname
------ -------------
1      Davis
2      Funk
3      Lew
4      Peled
5      Buck
6      Suurs
7      King
8      Cameron
9      Dolgopyatova
La cabecera de una relación es un conjunto de atributos, que se supone debe ser identificado por el nombre y el tipo del nombre. No hay un orden a los atributos. A la inversa, T-SQL mantiene un seguimiento de las posiciones ordinales de las columnas basadas en su orden de aparición en la definición de la tabla. Cuándo emite una consulta con SELECT *, está garantizando obtener las columnas en el resultado basados en el orden de definición. Además, T-SQL permite referirse a las posiciones ordinales de columnas del resultado en la cláusula ORDER BY, de la siguiente manera.
SELECT empid, lastname
FROM HR.Employees
ORDER BY 1;
Más allá del hecho de que esta práctica no es relacional, piense en la posibilidad de error, si en algún momento cambia la lista SELECT y olvida cambiar la lista ORDER BY consecuentemente. Por lo tanto, la recomendación es siempre indicar el nombre de los atributos que necesita ordenar.
T-SQL tiene otra desviación del modelo relacional, en que permite definir los resultados de columnas basadas en una expresión sin asignar un nombre a la columna de destino. Por ejemplo, la siguiente consulta es válida en T-SQL.
SELECT empid, firstname + ' ' + lastname
FROM HR.Employees;
Esta consulta genera la siguiente salida.
empid
------ ------------------
1       Sara Davis
2       Don Funk
3       Judy Lew
4       Yael Peled
5       Sven Buck
6       Paul Suurs
7       Russell King
8       Maria Cameron
9       Zoya Dolgopyatova
Pero de acuerdo al modelo relacional, todos los atributos deben tener nombres. Para que la consulta sea relacional, necesita asignar un alias al atributo de destino. Puede hacerlo utilizando la cláusula AS, de la siguiente manera.
SELECT empid, firstname + ' ' + lastname AS fullname
FROM HR.Employees;
Además, T-SQL permite que una consulta retorne varias columnas de resultado con el mismo nombre. Por ejemplo, considere una unión entre dos tablas, T1 y T2, ambas con una columna llamada keycol. T-SQL permite una lista SELECT parecida a la siguiente.
SELECT T1.keycol, T2.keycol ...
Para que el resultado sea relacional, todos los atributos deben tener nombres únicos, por lo que podría necesitar utilizar diferentes alias para los atributos de resultado, así como el siguiente.
SELECT T1.keycol AS clave1, T2.keycol AS clave2 ...
En cuanto a los predicados, siguiendo la ley del medio excluido en la lógica matemática, un predicado puede evaluarse como Verdadero o F​also. En otras palabras, los predicados se supone que utilizan la lógica de dos valores. Sin embargo, Codd ha querido reflejar la posibilidad de valores que faltan en su modelo. El hace referencia a dos tipos de valores faltantes: faltantes, pero aplicables y faltantes, pero inaplicables. Tome el atributo teléfono móvil de un empleado como ejemplo. Un valor faltante pero aplicable sería si un empleado tiene un teléfono móvil, pero no ha querido facilitar esta información, por ejemplo, por razones de privacidad. Un valor faltante, pero inaplicable, podría ser cuando el empleado simplemente no tiene un teléfono móvil. Según Codd, un lenguaje basado en su modelo debe proporcionar dos marcas diferentes para los dos casos. T-SQL, de nuevo, basado en el estándar SQL, implementa solo una marca de propósito general llamada NULL para cualquier tipo de valor faltante. Esto conduce a la lógica de predicados de tres valores. Es decir, cuando un predicado compara dos valores, por ejemplo, el mobilephone = '(425) 555-0136', si ambos están presentes, el resultado se evalúa como Verdadero o Falso. Pero si uno de ellos es NULL, el resultado se evalúa como un tercer valor lógico, Desconocido.
Note que algunos creen que un modelo relacional válido debería seguir la lógica de dos valores, y oponerse fuertemente al concepto de NULLs en SQL. Pero como se mencionó, el creador del modelo relacional creía en la idea de soportar los valores faltantes, y los predicados que se extienden más allá de la lógica de dos valores. Lo que es importante desde la perspectiva de la codificación con T-SQL es entender que si la base de datos que está consultando soporta NULLs, su tratamiento está lejos de ser trivial. Es decir, necesita entender cuidadosamente lo que sucede cuando están involucrados NULLs en los datos, que está manipulando con diversas construcciones de consulta, como filtrado, clasificación, agrupación, uniones, o intersecciones. Por lo tanto, con cada pedazo de código que escribe con T-SQL, debe preguntarse si los NULLs son posibles en los datos con los que está interactuando. Si la respuesta es sí, querrá asegurarse de que entiende el tratamiento de NULLs en su consulta, y garantizar que sus pruebas abordan el tratamiento de NULLs específicamente.

Usando la Terminología Correcta

El uso de la terminología refleja su conocimiento. Por lo tanto, debe hacer un esfuerzo para comprender y utilizar la terminología correcta. Cuando se habla de temas relacionados con T-SQL, la gente a menudo usa términos incorrectos. Y si eso no es suficiente, incluso cuando se dan cuenta de cuáles son los términos correctos, también necesitan entender las diferencias entre los términos en T-SQL y en el modelo relacional.
Como un ejemplo de términos incorrectos en T-SQL, la gente frecuentemente usa los términos "campo" y "registro" para referirse a lo que T-SQL llama "columna" y "fila", respectivamente. Los campos y los registros son físicos. Los campos son lo que tiene en las interfaces de usuario en aplicaciones cliente, y los registros son lo que tiene en los archivos y cursores. Las tablas son lógicas, y tienen filas y columnas lógicas.
Otro ejemplo de un término incorrecto se refiere a "los valores NULL". Un NULL es una marca para un valor faltante, no es un valor en sí. Por lo tanto, el uso correcto del término es "marca NULL" o simplemente "NULL".
Además de utilizar la terminología T-SQL correcta, también es importante comprender las diferencias entre los términos de T-SQL y de su contraparte relacional. Recuerde de la sección anterior que T-SQL intenta representar una relación con una tabla, una tupla con una fila, y un atributo con una columna; pero los conceptos de T-SQL y su contraparte relacional difieren en varias maneras. Siempre y cuando sea consciente de esas diferencias, puede, y debe, esforzarse por utilizar T-SQL de una manera relacional.

Ejercicio 1: Identificar Elementos No Relacionales en una Consulta

En este ejercicio, se le da una consulta. Su tarea es identificar los elementos no relacionales en la consulta.
1.    Abra el SQL Server Management Studio (SSMS) y conéctese a la base de datos de ejemplo TSQL2012.
2.    Escriba la consulta siguiente en la ventana de consulta y ejecútela.
SELECT custid, YEAR(orderdate)
FROM Sales.Orders
ORDER BY 1, 2;
Se obtiene el resultado siguiente, que se muestra en forma abreviada.
custid
----------- -----------
1          2007
1           2007
1           2007
1           2008
1           2008
1           2008
2           2006
2           2007
2           2007
2           2008
...
3.    Revise el código y su salida. La consulta se supone retorna para cada cliente y año de pedido, el ID Cliente (custid) y el año de pedido (YEAR(orderdate)). Note que no hay un requerimiento de orden de presentación de la consulta. ¿Puede identificar qué aspectos son no relacionales en la consulta?
Respuesta: La consulta no tiene ALIAS en la expresión YEAR(orderdate), así que no hay un nombre para el atributo de resultado. La consulta puede retornar duplicados. La consulta obliga a cierto orden de presentación al resultado y utiliza posiciones ordinales en la cláusula ORDER BY.

Ejercicio 2: Convertir la Consulta No Relacional a Relacional

En este ejercicio, trabajará con la consulta proporcionada en el ejercicio 1, como su punto de partida. Después identificará los elementos no relacionales en la consulta, necesita aplicar las revisiones apropiadas para que sea relacional.
·         En el paso 3 del ejercicio 1, ha identificado elementos no relacionales en la última consulta. Aplicar revisiones a la consulta para hacerla relacional.
Se requiere un número de revisiones para hacer la consulta relacional.
·         Defina un nombre de atributo por asignar un alias a la expresión YEAR(orderdate).
·         Agregue una cláusula DISTINCT para eliminar duplicados.
·         También, eliminar la cláusula ORDER BY para retornar un resultado relacional.
·         Incluso si hubiera un requerimiento de ordenamiento de presentación (no en este caso), no debería utilizar las posiciones ordinales; en cambio, use nombres de atributos. El código debería ser similar al siguiente.
SELECT DISTINCT custid, YEAR(orderdate) AS añopedido

FROM Sales.Orders;

1 comentario: