Tabla de Contenidos
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:
- Todos los hombres son mortales.
- Sócrates es un hombre.
- 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:
- ∀x (Hx → Mx)
- Hs
- ∴ 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:
- Obtendrá los tres pedidos más recientes.
- 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.
Piensa en Cloud
Microsoft está invirtiendo mucho en la tecnología de la nube, haciéndola más rica y más potente. Para algunas aplicaciones, es inevitable que la base de datos residirá en la nube en un momento u otro. Se dedica mucho esfuerzo a tener una base de código para las versiones de caja (en premisas) y de nube (SQL Azure), pero hay algunas diferencias que debe tener en cuenta. La comprensión de las diferencias puede afectar la forma de escribir el código si desea una migración fluida (si y cuando migra).
Libros en pantalla de SQL Server detalla las características de idioma que están o no están disponibles en SQL Azure. Aquí sólo quiero mencionar un par de ejemplos para darle una idea de lo que significa codificar con T-SQL contra una caja mientras se piensa en la nube.
En SQL Azure no hay soporte para montones, sino sólo árboles B. Esto tiene que ver con el sistema de replicación interno utilizado por SQL Azure para mantener varias copias de la base de datos con fines de recuperación de desastres. Hay implicaciones en el hecho de que los montones no son apoyados. Por ejemplo, como resultado, no se admite el comando SELECT INTO. Recuerde que SELECT INTO no copia índices del origen, incluido el índice agrupado. En otras palabras, SELECT INTO siempre crea un montón y por lo tanto no está soportado en SQL Azure. Cuando escribe un nuevo código, en lugar de utilizar SELECT INTO, si utiliza CREATE TABLE seguido de INSERT SELECT, su código estará preparado para la nube en este aspecto.
Otro ejemplo es el hecho de que en SQL Azure no se puede cambiar entre bases de datos dentro de la misma conexión. La instrucción USE es compatible, pero sólo funciona si ya está conectado a la base de datos de destino. Intente cambiar a una base de datos diferente y obtendrá un error. Con esto en mente, evite el código que alterna entre diferentes bases de datos si es posible.
El acceso directo a tempdb no está permitido en SQL Azure. Se le permite crear tablas temporales y declarar variables de tabla, pero no se le permite conectarse directamente a tempdb y crear objetos de usuario. Por lo tanto, debe evitar el acceso directo a tempdb.
Otro ejemplo de diferencia entre las versiones en caja y en nube de SQL Server es que en una versión de caja se lee el nivel de aislamiento predeterminado, mientras que en la nube se lee la instantánea confirmada. Comprender la diferencia entre los aislamientos es muy importante. Creo que la mayoría de los ambientes que usan read committed hoy en día en una versión de caja pueden beneficiarse enormemente de la transición a la lectura de instantáneas comprometidas. Por lo tanto, se recomienda que aprenda y entienda el valor de leer la instantánea y el plan comprometidos para la transición en algún momento. Entonces, por supuesto, la migración a la nube será más fácil.
Estos ejemplos ilustran lo que entiendo por “pensar en la nube” al codificar con T-SQL contra una caja. Para obtener más información sobre T-SQL en SQL Azure, consulte “ Referencia de Transact-SQL (Base de datos de SQL Azure) ”.
Mejores prácticas de fecha y hora
Existen muchas prácticas recomendadas relacionadas con el trabajo con datos de fecha y hora. Sin embargo, quiero mencionar dos malos hábitos particularmente comunes que recomiendo evitar.
Utilizar literales neutros en el lenguaje. Cuando se expresa literales de fecha, una práctica común es usar un formulario que dependa del idioma, ya que para eso se utiliza el desarrollador (por ejemplo, el formulario '01 / 02/11 '). Sin embargo, dependiendo del idioma del inicio de sesión conectado a SQL Server y ejecutando el código, este formulario puede interpretarse de varias maneras diferentes. Para un inicio de sesión con inglés de EE.UU., este formulario significaría mes / día / año; Para uno con inglés británico, significaría día / mes / año; Y para uno con japonés, significaría año / mes / día. Como desarrollador, no desea utilizar un formulario que se interpretará de forma diferente dependiendo del idioma del inicio de sesión que ejecute su código. La mejor práctica es usar un formulario que sea neutral en el lenguaje. Por ejemplo, cuando se utiliza '20110102', SQL Server siempre interpretará la fecha como YYYYMMDD porque este formulario es neutral del lenguaje. Es necesario tener especial cuidado con el formulario '2011-01-02', ya que para los tipos de fecha y hora más antiguos (DATETIME y SMALLDATETIME), depende del idioma (lea como YYYY-MM-DD para algunos y AAAA-DD- MM para otros), mientras que para los tipos más nuevos (DATE, DATETIME2 y DATETIMEOFFSET), siempre se interpreta como AAAA-MM-DD. Piense en usar este formulario en su código, y luego en algún momento alterando el tipo de su atributo. Su código podría cambiar repentinamente su significado. Por lo tanto, prefiero pegarme a la forma YYYYMMDD para todos los tipos - viejo y nuevo, donde siempre se interpreta de la misma manera.
Tenga cuidado con los errores de redondeo. Suponga que está utilizando el tipo de datos DATETIME para su columna y sus datos tienen valores de fecha y hora que no son necesariamente medianoche. Desea filtrar un período de datos de fecha y hora como enero de 2012. Algunas personas utilizan el siguiente formulario de filtro:
WHERE col BETWEEN '20120101' AND '20120131 23:59:59.999'
El problema es que 999 como la unidad de milisegundos no es una multiplicación de la unidad de precisión para DATETIME, que es de tres y un tercer milisegundos. Por lo tanto, el valor se redondea a la siguiente medianoche, y su rango podría terminar incluyendo filas que no se supone que incluya. Algunas personas “solucionan” este problema utilizando 997 en la unidad de milisegundos, pero ¿qué pasa si en algún momento en el futuro se altera el tipo a uno con precisión más fina? Por lo tanto, la mejor práctica con rangos de fecha y hora es evitar BETWEEN y utilizar siempre el formulario:
WHERE col >= '20120101' AND col < '20120201'
Este formulario funciona con todos los tipos y todas las precisiones, independientemente de si la parte de tiempo es aplicable.
Escribir código estándar
T-SQL tiene varios elementos de lenguaje patentados. Algunos tienen contrapartes que son compatibles con SQL estándar (ISO y ANSI). Encuentro que es importante hacer un esfuerzo para escribir el código estándar a menos que haya una razón substancial de hacer de otra manera, tal como funcionalidad adicional o mejor funcionamiento. Siguiendo el estándar le permite escribir código que sea más portátil entre plataformas. Pero no es sólo su código que se vuelve más portátil; Su conocimiento lo hace también.
En “ Prácticas recomendadas de T-SQL, Parte 1 ”, recomendé terminar las sentencias T-SQL con un punto y coma. También debe tratar de atenerse a las funciones estándar y los operadores. Por ejemplo, la función CAST es estándar, mientras que la función CONVERT no lo es. Si no está confiando en el argumento de estilo opcional que CONVERT admite pero CAST no lo hace, utilice CAST. COALESCE es estándar, mientras que ISNULL no lo es. COALESCE es más flexible que ISNULL, así que usarlo es una situación de ganar-ganar. El operador <> es estándar, mientras que el operador = no lo es - así que la recomendación obvia es usar <>.
Estos ejemplos simplemente le dan un sentido de las prácticas de codificación estándar. Es importante aprender lo que es estándar y lo que no es y seguir el estándar cuando sea posible.
Tenga cuidado con las reglas del pulgar
A la gente le gustan las reglas básicas, pero no siempre existen. He visto muchas de esas reglas que probablemente fueron creadas basadas en observaciones muy limitadas y luego hicieron una regla. ¿Ha oído reglas básicas como “Usar combinaciones porque son más eficientes que las subconsultas” o “Si tiene más de n tablas en una sola consulta, rompúela con varias consultas”? La realidad es que a veces las uniones son más eficientes que las subconsultas, a veces viceversa, ya veces se optimizan lo mismo. A veces, una sola consulta con 20 uniones funcionará muy bien y una consulta con 4 combinaciones funcionará mejor si se divide en pasos que rellenan las tablas temporales que se unen a continuación. La optimización del código de T-SQL es un proceso tan dinámico y sofisticado que las reglas del pulgar como éstas no ayudarán pero en lugar de otro le engañarán. Si usted insiste en adoptar una regla general, hágalo “Mantenga una mente abierta, y tenga cuidado con las reglas del pulgar.” Esto siempre debe ser cierto … tal vez …
Usar argumentos de búsqueda
Al optimizar una consulta y considerar si y cómo usar índices, uno de los elementos clave que el optimizador evalúa son los predicados en los filtros de consulta. Sin embargo, hay que tener en cuenta que para confiar en ordenar el índice (por ejemplo, realizar una búsqueda de índice o un escaneo ordenado), el predicado debe ser de una forma particular. Específicamente, con excepción de muy pocas excepciones, debe intentar evitar la manipulación de las columnas de clave de índice. En la mayoría de los casos, después de aplicar la manipulación, el optimizador no puede confiar en que el resultado de la manipulación conserva el orden del índice. A veces no hay mucho que puedas hacer para evitar la manipulación, pero a veces puedes hacerlo. Cuando el optimizador puede confiar en el ordenamiento de índice para un predicado dado, se dice que el predicado es un argumento de búsqueda, o SARG para abreviar. Aquí están algunos ejemplos:
- El predicado COALESCE (T1.col1, -1) = COALESCE (T2.col1, -1) no es un SARG, mientras que el predicado T1.col1 = T2.col1 OR (T1.col1 IS NULL y T2.col1 IS NULL ) es.
- El predicado col1 - 1 ⇐ @ n no es un SARG, mientras que el predicado col1 ⇐ @ n + 1 es.
- El predicado LEFT (col1, 3) no es un SARG, mientras que el predicado col1 LIKE 'abc%' es.
Como puede ver, a veces las revisiones simples de sus predicados pueden retener el significado y permitir un uso eficiente de la indexación.