¡Esta es una revisión vieja del documento!


T-SQL

Teoria de conjuntos

La teoría de conjuntos más elemental es una de las herramientas básicas del lenguaje matemático. Dados unos elementos, unos objetos matemáticos como números o polígonos por ejemplo, puede imaginarse una colección determinada de estos objetos, un conjunto. Cada uno de estos elementos pertenece al conjunto, y esta noción de pertenencia es la relación relativa a conjuntos más básica. Los propios conjuntos pueden imaginarse a su vez como elementos de otros conjuntos. La pertenencia de un elemento a a un conjunto A se indica como a ∈ A.

Una relación entre conjuntos derivada de la relación de pertenencia es la relación de inclusión. Una subcolección de elementos B de un conjunto dado A es un subconjunto de A, y se indica como B ⊆ A.

Ejemplos

  • Los conjuntos numéricos usuales en matemáticas son: el conjunto de los números naturales N, el de los números enteros Z, el de los números racionales Q, el de los números reales R y el de los números complejos C. Cada uno es subconjunto del siguiente:

N ᑕ Z ᑕ Q ᑕ R ᑕ C

  • El espacio tridimensional E3 es un conjunto de objetos elementales denominados puntos p, p ∈ E3. Las rectas r y planos α son conjuntos de puntos a su vez, y en particular son subconjuntos de E3, r ⊆ E3 y α ⊆ E3.

Álgebra de conjuntos

Existen unas operaciones básicas que permiten manipular los conjuntos y sus elementos, similares a las operaciones aritméticas, constituyendo el álgebra de conjuntos:

  • Unión. La unión de dos conjuntos A y B es el conjunto A ∪ B que contiene cada elemento que está por lo menos en uno de ellos.
  • Intersección. La intersección de dos conjuntos A y B es el conjunto A ∩ B que contiene todos los elementos comunes de A y B.
  • Diferencia. La diferencia entre dos conjuntos A y B es el conjunto A \ B que contiene todos los elementos de A que no pertenecen a B.
  • Complemento. El complemento de un conjunto A es el conjunto A∁ que contiene todos los elementos (respecto de algún conjunto referencial) que no pertenecen a A.
  • Diferencia simétrica. La diferencia simétrica de dos conjuntos A y B es el conjunto A Δ B con todos los elementos que pertenecen, o bien a A, o bien a B, pero no a ambos a la vez.
  • Producto cartesiano. El producto cartesiano de dos conjuntos A y B es el conjunto A × B que contiene todos los pares ordenados (a, b) cuyo primer elemento a pertenece a A y su segundo elemento b pertenece a B.

Lógica de Predicados

Es un sistema formal diseñado para estudiar la inferencia en los lenguajes de primer orden.1 Los lenguajes de primer orden son, a su vez, lenguajes formales con cuantificadores que alcanzan sólo a variables de individuo, y con predicados y funciones cuyos argumentos son sólo constantes o variables de individuo.

Predicados

Un predicado es una expresión lingüística que puede conectarse con una o varias otras expresiones para formar una oración. Por ejemplo, en la oración «Marte es un planeta», la expresión «es un planeta» es un predicado que se conecta con la expresión «Marte» para formar una oración. Y en la oración «Júpiter es más grande que Marte», la expresión «es más grande que» es un predicado que se conecta con dos expresiones, «Júpiter» y «Marte», para formar una oración.

En lógica matemática, cuando un predicado se conecta con una expresión, se dice que expresa una propiedad (como la propiedad de ser un planeta), y cuando se conecta con dos o más expresiones, se dice que expresa una relación (como la relación de ser más grande que). Sin embargo, la lógica de primer orden no hace ningún supuesto sobre si existen o no las propiedades o las relaciones. Sólo se ocupa de estudiar el modo en que hablamos y razonamos con expresiones lingúisticas.

En la lógica de primer orden, los predicados son tratados como funciones. Una función es, metafóricamente hablando, una máquina que recibe un conjunto de cosas, las procesa, y devuelve como resultado una única cosa. A las cosas que entran a las funciones se las llama argumentos,4 y a las cosas que salen, valores o imágenes. Considérese por ejemplo la siguiente función matemática:

f(x) = 2x

Esta función toma números como argumentos y devuelve más números como valores. Por ejemplo, si toma el número 1, devuelve el número 2, y si toma el 5, devuelve el 10. En la lógica de primer orden, se propone tratar a los predicados como funciones que no sólo toman números como argumentos, sino expresiones como «Marte», «Mercurio» y otras que se verán más adelante. De este modo, la oración «Marte es un planeta» puede transcribirse, siguiendo la notación propia de las funciones, de la siguiente manera:

Planeta(Marte)

O, más abreviadamente:

P(m)

En la matemática existen además funciones que toman varios argumentos. Por ejemplo:

f(x,y) = x + y

Esta función, si toma los números 1 y 2, devuelve el número 3, y si toma el -5 y el -3, devuelve el -8. Siguiendo esta idea, la lógica de primer orden trata a los predicados que expresan relaciones, como funciones que toman dos o más argumentos. Por ejemplo, la oración «Caín mató a Abel» puede formalizarse así:

Mató(Caín,Abel)

O abreviando:

M(c,a)

Este procedimiento puede extenderse para tratar con predicados que expresan relaciones entre muchas entidades. Por ejemplo, la oración «Ana está sentada entre Bruno y Carlos» puede formalizarse:

S(a,b,c)

Constantes de individuo

Una constante de individuo es una expresión lingüística que refiere a una entidad. Por ejemplo «Marte», «Júpiter», «Caín» y «Abel» son constantes de individuo. También lo son las expresiones «1», «2», etc., que refieren a números. Una entidad no tiene que existir para que se pueda hablar acerca de ella, de modo que la lógica de primer orden tampoco hace supuestos acerca de la existencia o no de las entidades a las que refieren sus constantes de individuo.

Variables de individuo

Además de las constantes de individuo que hacen referencia a entidades determinadas, la lógica de primer orden cuenta con otras expresiones, las variables, cuya referencia no está determinada. Su función es similar a la de las expresiones del lenguaje natural como «él», «ella», «esto», «eso» y «aquello», cuyo referente varía con el contexto. Las variables generalmente se representan con letras minúsculas cerca del final del alfabeto latino, principalmente la x, y y z. Del mismo modo, en la matemática, la x en la función f(x) = 2x no representa ningún número en particular, sino que es algo así como un espacio vacío donde pueden insertarse distintos números. En conclusión, podemos representar una expresión como «esto es antiguo» con la expresión:

Antiguo(x)

O abreviadamente:

A(x)

Es evidente, sin embargo, que hasta que no se determine a qué refiere la x, no es posible asignar un valor de verdad a la expresión «esto es antiguo», del mismo modo que hasta que no se determine un número para la x en la función f(x) = 2x, no será posible calcular ningún valor para la función.

Por supuesto, al igual que con las constantes de individuo, las variables sirven también para formalizar relaciones. Por ejemplo, la oración «esto es más grande que aquello» se formaliza:

G(x,y)

Y también pueden combinarse constantes de individuo con variables. Por ejemplo en la oración «ella está sentada entre Bruno y Carlos»:

S(x,b,c)

Cuantificadores

Considérese ahora la siguiente expresión matemática:

x > 3

Esta expresión no es ni verdadera ni falsa, y parece que no lo será hasta que no reemplacemos a la x por algún número cualquiera. Sin embargo, también es posible dar un valor de verdad a la expresión si se le antepone un cuantificador. Un cuantificador es un operador sobre un conjunto de individuos, se trata de un recurso expresivo que permite construir proposiciones sobre conjuntos o dicho de otra forma,5 un cuantificador es una expresión que afirma que una condición se cumple para un cierto número de individuos.6 En la lógica clásica, los dos cuantificadores más estudiados son el cuantificador universal y el cuantificador existencial.6 El primero afirma que una condición se cumple para todos los individuos de los que se está hablando,6 y el segundo que se cumple para al menos uno de los individuos.6 Por ejemplo, la expresión “para todo x” es un cuantificador universal, que antepuesto a “x < 3”, produce:

Para todo x, x < 3

Esta es una expresión con valor de verdad, en particular, una expresión falsa, pues existen muchos números (muchos x) que son mayores que tres. Anteponiendo en cambio la expresión “para al menos un x”, un cuantificador existencial, se obtiene:

Para al menos un x, x < 3

La cual resulta ser una expresión verdadera.

Adviértase ahora, sin embargo, que el valor de verdad de las dos expresiones anteriores depende de qué números se esté hablando. Si cuando se afirma “para todo x, x < 3”, se está hablando sólo de los números negativos, por ejemplo, entonces la afirmación es verdadera. Y si al afirmar “para al menos un x, x < 3” se está hablando solamente de los números 3, 4 y 5, entonces la afirmación es falsa. En lógica, a aquello de lo que se está hablando cuando se usa algún cuantificador, se lo llama el dominio de discurso.7

Esta maquinaria puede adaptarse fácilmente para formalizar oraciones con cuantificadores del lenguaje natural. Tómese por caso la afirmación “todos son amigables”. Esta oración puede traducirse así:

Para todo x, x es amigable.

Y una oración como “alguien está mintiendo” puede traducirse:

Para al menos un x, x está mintiendo.

También es frecuente traducir esta última oración así:

Existe al menos un x, tal que x está mintiendo.

A continuación se formalizan ambas oraciones, introduciendo a la vez la notación especial para los cuantificadores:

Para todo x, x es amigable. ∀x A(x)
Existe al menos un x, tal que x está mintiendo. ∃x M(x)

Conectivas

La lógica de primer orden incorpora además las conectivas de la lógica proposicional. Combinando las conectivas con los predicados, constantes, variables y cuantificadores, es posible formalizar oraciones como las siguientes:

Oración Formalización
Sócrates es sabio y prudente. Ss ∧ Ps
Si Sócrates es sabio, entonces también es prudente. Ss → Ps
Nadie es sabio y además prudente. ¬∃x (Sx ∧ Px)
Todos los sabios son prudentes. ∀x (Sx → Px)

Argumentos

Considérese el siguiente argumento clásico:

  1. Todos los hombres son mortales.
  2. Sócrates es un hombre.
  3. Por lo tanto, Sócrates es mortal.

La tarea de la lógica de primer orden consiste en determinar por qué los argumentos como éste resultan válidos. Para eso, el primer paso es traducirlos a un lenguaje más preciso, que pueda ser analizado mediante métodos formales. Según lo visto más arriba, la formalización de este argumento es la siguiente:

  1. ∀x (Hx → Mx)
  2. Hs
  3. ∴ Ms

Fundamentos de T-SQL: Pensando en Conjuntos

Un conjunto como un todo

Cuando interactúas con un conjunto, debes pensarlo como un todo, no como elementos individuales. Por ejemplo, al escribir consultas SQL, interactúa con la tabla de entrada como un todo y no con sus filas individuales. Eso está basado en el sistema. Cuando se utiliza un cursor, se trata de una fila a la vez. Eso no está establecido.

Algunas personas piensan que si no utiliza explícitamente un cursor, la solución debe estar basada en la configuración. Consideremos, por ejemplo, una solución que implementa la siguiente lógica, expresada en pseudo código:

Recuperar la parte superior ( 1 ) ORDEN DE FILA POR TECLA

Mientras que CURRENT KEY NO ES NULO

EMPEZAR

Proceso FILA CON LLAVE CORRIENTE

Recuperar la parte superior ( 1 ) FILA

DÓNDE CLAVE > ORDEN CLAVE ACTUAL POR TECLA

FIN

Aunque este proceso iterativo no utiliza un objeto CURSOR T-SQL, se ocupa de una fila a la vez. Es mucho más caro que un cursor real en términos de su huella de E / S. Al menos con el cursor, realiza un paso sobre los datos, por lo que su impresión de E / S es mucho menor.

Ya he discutido las razones filosóficas para el uso de soluciones basadas en set. También hay una razón más práctica: el rendimiento. La implementación de construcciones iterativas de T-SQL de SQL Server como el bucle WHILE es muy ineficiente. Si ejecuta un bucle WHILE para un millón de iteraciones en T-SQL y un bucle WHILE para un millón de iteraciones en un lenguaje procedural como C #, verá una diferencia sorprendente en el rendimiento. En mi computadora portátil, el bucle T-SQL WHILE tardó más de 100 segundos en completarse, mientras que el bucle C # WHILE tomó menos de 10 segundos. Esa es una diferencia de orden de magnitud. Por lo tanto, las iteraciones en T-SQL son lentas.

El procesamiento de cursor también se implementa ineficientemente en T-SQL. Usted paga una sobrecarga adicional por cada procesamiento de registro del cursor que no paga cuando procesa el mismo número de filas con una consulta basada en conjuntos. Si desea ver la cantidad de sobrecarga, puede realizar esta prueba básica: emita una consulta SELECT * simple en una tabla suficientemente grande (por ejemplo, unos pocos millones de filas), midiendo el tiempo de ejecución de la consulta basada en conjuntos (con los resultados descartados ). A continuación, repita esta prueba, excepto que esta vez use un cursor en lugar de una consulta basada en un conjunto. Puede encontrar una comparación de rendimiento con ejemplos de código en el capítulo de muestra gratuito del libro Inside Microsoft SQL Server 2005: Programación T-SQL . Puede descargar “ Capítulo 03 - Cursores ”. Verá que el código del cursor se ejecuta varias docenas de veces más lento en comparación con el código basado en set.

Para entender por qué esto es cierto, suponga que llama al coste de la implementación de SQL Server de analizar n filas con una consulta basada en conjuntos n . A continuación, tomando el coste adicional para cada procesamiento de registros con el cursor (llamarlo o para la sobrecarga del cursor por fila), el coste de escanear n filas con un cursor se puede describir como sigue:

N + ( n 'o )

Por el momento, la parte o es bastante alta. Como mencioné anteriormente, el código del cursor se ejecuta varias docenas de veces más lento que el código basado en set. Esto significa que usar un cursor como su punto de partida pone su código en una desventaja.

Distinto

Según la definición de Cantor, un conjunto tiene elementos distintos. Codd describió de manera divertida el requisito distinto diciendo que si un hecho es verdadero, decirlo dos veces no lo hace más verdadero. SQL se desvía de la teoría de conjuntos (y por lo tanto del modelo relacional) porque permite duplicados en una tabla si no hace cumplir una clave. E incluso si tiene una clave en una tabla, una consulta todavía puede devolver duplicados. Por ejemplo, considere la consulta

USE AdventureWorks2008R2; 
SELECT TerritoryID 
FROM Ventas.SalesOrderHeader;

La consulta está buscando los territorios donde se realizaron los pedidos. SQL (o en este caso, T-SQL) no intenta eliminar duplicados, y que no es relacional. Un lenguaje verdaderamente relacional no devuelve duplicados.

SQL proporciona herramientas para acercarse a escribir código de una manera relacional. Por lo tanto, es más preciso decir que SQL se basa en la teoría de conjuntos múltiples en oposición a la teoría de conjuntos. Si son posibles duplicados en el resultado, puede eliminarlos agregando una cláusula DISTINCT, como en

SELECT DISTINCT TerritoryID 
FROM Ventas.SalesOrderHeader;

Algunas personas recomiendan agregar una cláusula DISTINCT a todas las consultas. Sin embargo, en los casos en que no existen duplicados para empezar, agregar una cláusula DISTINCT podría hacer que SQL Server realizar trabajo adicional en un intento de eliminar duplicados. Por lo tanto, recomiendo añadir DISTINCT sólo en los casos en que los duplicados pueden aparecer en el resultado.

Orden

Uno de los aspectos más importantes de los conjuntos está implícito en la definición de Cantor. No menciona el orden de los elementos en un conjunto porque el orden no es importante. Ese es uno de los conceptos más difíciles de entender por la gente al consultar una tabla, es decir, entender que no hay garantías de que los datos que se están consultando se consumirán en un orden determinado. Por ejemplo, algunas personas asumen que cuando consultan una tabla que tiene un índice agrupado, obtendrán los datos en orden de índice agrupado. Desde el punto de vista del lenguaje, eso no está garantizado porque lo que estás preguntando es un conjunto y lo que recuperas es un conjunto. SQL Server sabe que el lenguaje no proporciona garantías de pedido, por lo que analiza los datos en cualquier orden que le guste, incluso no en orden de clúster agrupado. La gente a menudo utiliza técnicas que violan los conceptos basados ​​en conjuntos y relacionales en el sentido de que la exactitud de los resultados depende de los datos que se consumen en el orden de los índices, lo que SQL Server nunca ha garantizado. Abrigo un par de ejemplos clásicos en mis artículos exclusivos de la red “Asignación de variables de varias filas y ORDER BY ” y “ UPDATE ordenado y soluciones basadas en conjuntos para ejecutar agregados ”.

Por lo tanto, si un conjunto no tiene ningún orden para sus elementos, ¿por qué SQL soporta una cláusula ORDER BY? La respuesta es que tradicionalmente la cláusula ORDER BY cumplía una función de ordenación de presentación. Más tarde se sobrecargó con otros papeles, de los que hablaré en breve. Una consulta con una cláusula ORDER BY realmente no devuelve un resultado relacional, sino más bien qué SQL estándar llama a un cursor. Conceptualmente, un cursor es un resultado que soporta una propiedad de ordenación. Si lo desea, puede iterar a través de sus registros uno a la vez.

Si alguna vez te has preguntado por qué T-SQL no permite definir una expresión de tabla (por ejemplo, vista, tabla derivada, función de tabla en línea, expresión de tabla común-CTE) basada en una consulta con una cláusula ORDER BY, la respuesta debe ahora Ser claro: Se supone que una expresión de tabla representa una tabla. Se supone que una tabla representa una relación. (Si no está familiarizado con la terminología relacional, consulte la barra lateral “Términos de gestión de datos comúnmente mal utilizados”.) Una relación es un conjunto. Un conjunto no tiene orden garantizado a sus elementos.

La adición del filtro TOP a T-SQL ha traído mucha más confusión. TOP le permite filtrar datos basados ​​en varias filas y pedidos. Nada en este filtro viola conceptualmente los conceptos relacionales; Es sólo un filtro. Sin embargo, la implementación del filtro TOP no permite especificar su propio orden, sino que sobrecarga la cláusula ORDER BY existente con esta función adicional. Por lo tanto, cuando tiene una consulta como

SELECT TOP (3) SalesOrderID, OrderDate, CustomerID 
FROM Ventas.SalesOrderHeader 
ORDER BY OrderDate DESC, SalesOrderID DESC ;

La cláusula ORDER BY proporciona dos garantías:

  1. Obtendrá los tres pedidos más recientes.
  2. Las filas en el resultado se presentarán de la más reciente a la menos reciente.

Desafortunadamente, no puede separar el orden de filtro TOP de la ordenación de la presentación directamente. La misma limitación se aplica al filtro OFFSET / FETCH estándar que se está introduciendo en la próxima versión de SQL Server, que tiene el nombre de código Denali, cuando se utiliza en código como

SELECT SalesOrderID, OrderDate, CustomerId
FROM Ventas.SalesOrderHeader 
ORDER BY OrderDate DESC, SalesOrderID DESC 
OFFSET 0 ROWS 
FETCH FIRST 3 ROWS ONLY;

¿Cómo se define una expresión de tabla basada en una consulta que tiene un filtro de este tipo si el filtro obliga a ordenar la presentación y al hacerlo viola las propiedades relacionales que se supone que la expresión de tabla posea? Tanto T-SQL como SQL estándar decidieron resolver este problema permitiéndole utilizar una cláusula ORDER BY en una consulta que define una expresión de tabla cuando también se especifica TOP o OFFSET / FETCH. Pero lo que mucha gente no se da cuenta es que una consulta externa contra la expresión de tabla no garantiza el orden de presentación a menos que añada una cláusula ORDER BY en esa consulta externa. Si entiendes conceptos relacionales, esto tiene perfecto sentido. Sin embargo, hubiera sido mucho mejor si el diseño de TOP y OFFSET / FETCH le hubiera permitido especificar un pedido específico para el filtro y no vinculado a la ordenación de la presentación.

Un gran ejemplo de un elemento de lenguaje bellamente diseñado que tiene orden como parte de la especificación del cálculo, sin depender de los datos que se consumen en cierto orden o sin atar el orden del cálculo al ordenamiento de la presentación, son las funciones de ventana. (Las funciones de ventana son un subconjunto de las funciones de conjunto ordenadas en el SQL estándar). Por ejemplo, para calcular un atributo de rango para las filas en función del orden de la cantidad, se utilizaría

RANK ( ) OVER ( ORDER BY qty ) AS rnk

La entrada a la consulta es un conjunto, y la salida es un conjunto. No está obligado a agregar orden de presentación a la consulta. Si decide agregar orden de presentación, no tiene que ser el mismo que el pedido en la función de ventana. Esa es una gran característica de diseño. Si TOP y OFFSET / FETCH hubieran sido diseñados de manera similar, habría mucha menos confusión sobre su uso.

Origenes de Información