lunes, 22 de febrero de 2016

Filtrando Datos con Predicados

T-SQL soporta tres cláusulas de consulta que permiten filtrar los datos basados en los predicados. Estas son las cláusulas ON, WHERE y HAVING. Este post se centra en los datos de filtrado con la cláusula WHERE.

Predicados, Lógica de Tres Valores, y Argumentos de Búsqueda

En las primeras consultas SQL que alguna vez escribió, es muy probable que haya comenzado utilizando la cláusula WHERE para filtrar los datos basados en los predicados. Inicialmente, parece un concepto muy simple y directo. Pero con el tiempo, a medida que adquiera una comprensión más profunda de T-SQL, probablemente se dará cuenta de que hay aspectos del filtrado que no son tan evidentes. Por ejemplo, necesita entender cómo interactúan los predicados con los NULLs, y cómo filtra basados en tales predicados. También es necesario entender cómo formar sus predicados para maximizar la eficiencia de sus consultas, y para ello, necesita estar familiarizado con el concepto de argumento de búsqueda.
Algunos de los ejemplos en este post utilizan la tabla HR.Employees de la base de datos de muestra TSQL2012.
Para comenzar con un ejemplo simple, considere la siguiente consulta, que filtra sólo a los empleados de los Estados Unidos.
SELECT empid, firstname, lastname, country, region, city
FROM HR.Employees
WHERE country = N'USA';
Recuerde que un predicado es una expresión lógica. Cuando los NULLs no son posibles en los datos (en este caso, la columna country es definida para no permitir NULLs), el predicado puede ser evaluado a Verdadero o Falso. El tipo de lógica utilizada en tal caso es conocida como lógica de dos valores. El filtro WHERE retorna solo las filas para el cual el predicado se evalúa a Verdadero. Aquí está el resultado de esta consulta.
empid  firstname  lastname  country  region  city
------ ---------- --------- -------- ------- ---------
1       Sara        Davis    USA       WA     Seattle
2       Don         Funk      USA       WA     Tacoma
3       Judy        Lew       USA       WA     Kirkland
4       Yael        Peled    USA       WA     Redmond
8       Maria       Cameron   USA       WA     Seattle
Sin embargo, cuando los NULLs son posibles en los datos, las cosas se vuelven más complicadas. Considere las columnas de localización de clientes, país, región y ciudad en la tabla Sales.Customers. Suponga que estas columnas reflejan la jerarquía de localización basada en la organización de ventas. Para algunos lugares en el mundo, como en los Estados Unidos, las tres columnas de ubicación son aplicables; por ejemplo:
Country:     USA
Region:      WA
City:        Seattle
Pero en otros lugares, como el Reino Unido, sólo tienen dos partes aplicables: el país y la ciudad. En tales casos, la columna región es establecida a NULL; por ejemplo:
Country:     UK
Region:      NULL
City:        London
Considere entonces una consulta que filtra sólo los empleados del estado de Washington.
SELECT empid, firstname, lastname, country, region, city
FROM HR.Employees
WHERE region = N'WA';
Recordemos que cuando los NULLs son posibles en los datos, un predicado puede evaluarse a Verdadero, Falso, y Desconocido. Este tipo de lógica es conocida como la lógica de tres valores. Cuando se utiliza un operador de igualdad en el predicado como en la consulta anterior, se obtiene Verdadero cuando ambos operandos no son NULL e iguales; por ejemplo, WA y WA. Obtiene Falso cuando ambos no son NULL y diferentes; por ejemplo, OR y WA. Hasta ahora, es sencillo. La parte difícil es cuando las marcas NULL están involucradas. Obtiene un Desconocido cuando al menos un operando es NULL; por ejemplo, NULL y WA, o incluso NULL y NULL.
Como se mencionó, el filtro WHERE retorna las filas para el cual el predicado se evalúa a Verdadero, significando que descarta ambos casos Falso y Desconocido. Por lo tanto, la consulta retorna sólo los empleados donde la región es no NULL e igual a WA, como se muestra en la siguiente.
empid  firstname  lastname  country  region  city
------ ---------- --------- -------- ------- ---------
1       Sara        Davis    USA       WA     Seattle
2       Don         Funk      USA       WA     Tacoma
3       Judy        Lew       USA       WA     Kirkland
4       Yael        Peled    USA       WA     Redmond
8       Maria       Cameron   USA       WA     Seattle
Podría considerar este comportamiento como intuitivo, pero considere una solicitud para retornar sólo los empleados que no son del estado de Washington.
Escribir la siguiente consulta:
SELECT empid, firstname, lastname, country, region, city
FROM HR.Employees
WHERE region <> N'WA';
Ejecute la consulta y obtiene un conjunto vacío de vuelta:
empid  firstname  lastname  country  region  city
------ ---------- --------- -------- ------- ---------
¿Puede tener sentido el resultado?
Como resultado, todos los empleados que no son del estado de Washington, son de UK; recordamos que la región de lugares en UK es establecida a NULL para indicar que es inaplicable. Aunque puede serle claro que alguien de UK no es del estado de Washington, no le es claro a T-SQL. Para T-SQL, un NULL representa un valor faltante que podría ser aplicable, y podría ser WA como podría ser algo más. Por lo tanto, no puede concluir con certeza que la región es diferente de WA. En otras palabras, cuando la región es NULL, el predicado region <> 'WA' es evaluado a Desconocido, y la fila es descartada. Así como un predicado podría retornar sólo los casos que no son NULL y es conocido que son diferentes de WA. Por ejemplo, si tenía un empleado en la tabla con una región NY, tal empleado debería ser retornado.
Sabiendo que en la tabla Employees una región NULL representa una región faltante e inaplicable, ¿Cómo hacemos que T-SQL retorne tales empleados cuando busca lugares donde la región es diferente de WA?
Si está considerando un predicado tal como region <> N'WA' OR region = NULL, necesita recordar que dos NULLs no son considerados igual a otro. El resultado de la expresión NULL = NULL es, de hecho, Desconocido, no es Verdadero. T-SQL proporciona el predicado IS NULL para retornar Verdadero cuando el operando evaluado es NULL. Del mismo modo, el predicado IS NOT NULL retorna Verdadero cuando el operando evaluado no es NULL. Así que la solución a este problema es utilizar la siguiente forma.
SELECT empid, firstname, lastname, country, region, city
FROM HR.Employees
WHERE region <> N'WA'
  OR region IS NULL;
Aquí está el resultado de esta consulta.
empid  firstname  lastname        country  region  city
------ ---------- ------------- -------- ------- -------
5       Sven        Buck         UK        NULL    London
6       Paul        Suurs        UK        NULL    London
7       Russell     King         UK        NULL    London
9       Zoya        Dolgopyatova UK        NULL    London
Los filtros de consulta tienen un rendimiento importante a su lado. Por un lado, por filtrar las filas en la consulta (en lugar de hacerlo en el cliente), se reduce el tráfico de red. Además, basados en los filtros de consulta que aparecen en la consulta, SQL Server puede evaluar la opción para utilizar índices, para obtener los datos eficientemente, sin requerir una exploración completa de la tabla. Es importante señalar, sin embargo, que el predicado necesita ser de una forma conocida, como un argumento de búsqueda (SARG) para permitir el uso eficiente del índice. Aquí, se describirá brevemente el concepto y se proporciona ejemplos sencillos.
Un predicado en la forma de columna operador valor o valor operador columna puede ser un argumento de búsqueda. Por ejemplo, los predicados como col1=10, y col1>10 son argumentos de búsqueda. Aplicar la manipulación a la columna filtrada en la mayoría de los casos, impide al predicado que sea un argumento de búsqueda. Un ejemplo para la manipulación de la columna filtrada es aplicarle una función, como en F(col1)=10, donde F es alguna función. Hay algunas excepciones a esta regla, pero no son muy comunes.
Por ejemplo, suponga que tiene un procedimiento almacenado que acepta un parámetro de entrada @dt representando una fecha de envío de entrada. El procedimiento supuestamente retorna pedidos que fueron enviados en la fecha de entrada. Si la columna shippeddate no permite NULLs, puede utilizar la siguiente consulta para dirigir esta tarea.
SELECT orderid, orderdate, empid
FROM Sales.Orders
WHERE shippeddate = @dt;
Sin embargo, la columna shippeddate permite NULLs; estos representan pedidos que no fueron enviados todavía. Cuando los usuarios necesitaran todos los pedidos que aún no fueron enviados, los usuarios proporcionarán un NULL como la fecha de envío de entrada, y su consulta podría necesitar hacer frente a estos casos. Recuerde que cuando comparamos dos NULLs, obtenemos Desconocido y la fila es filtrada. Así que la forma actual del predicado no direcciona las entradas NULL correctamente. Algunos dirigen esta necesidad utilizando COALESCE o ISNULL para sustituir los NULLs con un valor que no existe en los datos normalmente, como en la siguiente.
SELECT orderid, orderdate, empid
FROM Sales.Orders
WHERE COALESCE(shippeddate, '19000101') = COALESCE(@dt, '19000101');
El problema es que aunque la solución ahora retorna el resultado correcto, incluso cuando la entrada es NULL, el predicado no es un argumento de búsqueda. Esto significa que SQL Server no puede utilizar eficientemente un índice en la columna shippeddate. Para hacer del predicado un argumento de búsqueda, es necesario evitar la manipulación de la columna filtrada y reescribir el predicado como la siguiente.
SELECT orderid, orderdate, empid
FROM Sales.Orders
WHERE shippeddate = @dt
OR (shippeddate IS NULL AND @dt IS NULL);
Curiosamente, el SQL estándar tiene un predicado llamado IS NOT DISTINCT FROM que tiene el mismo significado que el predicado utilizado en la última consulta (retornar Verdadero cuando ambos lados son iguales o cuando ambas son NULLs, que de otra manera es Falso). Desafortunadamente, T-SQL no soporta este predicado.
Otro ejemplo de manipulación implica la columna filtrada en una expresión; por ejemplo, col1-1 <= @n. A veces, puede reescribir el predicado a una forma que sea un argumento de búsqueda, y que luego permita el uso eficiente de indexación. El último predicado, por ejemplo, puede ser reescrito usando matemáticas simples como col1<= @n+1.
En resumen, cuando un predicado involucra manipulación de la columna filtrada, y hay formas alternativas para expresarlo sin la manipulación, puede aumentar la probabilidad para un uso eficiente de la indexación.

Combinando Predicados

Puede combinar predicados en la cláusula WHERE por utilizar los operadores lógicos AND y OR. También puede negar predicados por utilizar el operador lógico NOT. Esta sección comienza por describir los aspectos importantes de la negación y luego discute los predicados combinados.
La negación de Verdadero y Falso es sencilla, NOT Verdadero es Falso, y NOT Falso es Verdadero. ¿Lo qué puede sorprenderle a algunos, es lo que sucede cuando niegas Desconocido, NOT Desconocido aún es Desconocido.
Recuerde que anteriormente la consulta que retornó todos los empleados del estado de Washington; la consulta utilizó el predicado region=N'WA' en la cláusula WHERE. Suponga que quiere retornar los empleados que no son de WA, y para ello utiliza el predicado NOT region=N'WA'. Está claro que los casos que retornan Falso del predicado positivo (dicen que la región es NY) retornan Verdadero del predicado negativo. También está claro que los casos que retornan Verdadero del predicado positivo (dicen que la región es WA) retornan Falso del predicado negativo. Sin embargo, cuando la región es NULL, ambos el predicado positivo y negativo retorna Desconocido y la fila es descartada. Así que la manera correcta para que incluya casos NULL en el resultado, si es que sabe lo que necesita hacer, es utilizar el operador IS NULL, como en NOT region=N'WA' OR region IS NULL.
En cuanto a la combinación de predicados, hay varias cosas interesantes a notar. Algunas reglas de precedencia determinan el orden de evaluación lógica de los diferentes predicados. El operador NOT precede a AND y OR, y AND precede a OR. Por ejemplo, suponga que el filtro WHERE en su consulta tiene la siguiente combinación de predicados.
WHERE col1 = 'w' AND col2 = 'x' OR col3 = 'y' AND col4 = 'z'
Debido a que AND precede a OR, se obtiene el equivalente al siguiente.
WHERE (col1 = 'w' AND col2 = 'x') OR (col3 = 'y' AND col4 = 'z')
Tratando de expresar los operadores como seudo funciones, esta combinación de operadores es equivalente a OR(AND(col1='w', col2='x'), AND(col3='y', col4='z')).
Debido a que los paréntesis tienen la precedencia más alta entre todos los operadores, siempre puede utilizarlos para controlar totalmente el orden de evaluación lógica que necesita, como muestra el siguiente ejemplo.
WHERE col1 = 'w' AND (col2 = 'x' OR col3 = 'y') AND col4 = 'z'
Una vez más, usando seudo funciones, esta combinación de operadores y el uso de paréntesis es equivalente a AND(col1='w', OR(col2='x', col3='y'), col4='z').
Recuerde que todas las expresiones que aparecen en la misma fase de procesamiento de consulta lógica, por ejemplo, la fase WHERE, es conceptualmente evaluada en el mismo punto en el tiempo. Por ejemplo, considere el siguiente predicado de filtro.
WHERE propertytype = 'INT' AND CAST(propertyval AS INT) > 10
Suponga que la tabla que está siendo consultada mantiene diferentes valores de propiedad. La columna propertytype representa el tipo de propiedad (un INT, un DATE, y así sucesivamente), y la columna propertyval mantiene el valor en una cadena de caracteres. Cuando el propertytype es 'INT', el valor en propertyval es convertible a INT; de otra forma, no necesariamente.
Algunos asumen que a menos que las reglas de precedencia dicten lo contrario, los predicados serán evaluados de izquierda a derecha, y que los cortocircuitos tendrán lugar cuando sea posible. En otras palabras, si el primer predicado propertytype='INT' se evalúa a Falso, SQL Server no evalúa el segundo predicado CAST(propertyval AS INT)>10 porque el resultado es ya conocido. Basado en este supuesto, la expectativa es que la consulta nunca debería fallar, tratando de convertir algo que no es convertible.
La realidad, sin embargo, es diferente. SQL Server soporta internamente un concepto de cortocircuito; sin embargo, debido al concepto de todo en uno en el lenguaje, no necesariamente evaluará las expresiones en orden de izquierda a derecha. Podría decidir, basado en razones relativo a costos, comenzar con la segunda expresión, y luego, si la segunda expresión se evalúa a Verdadero, evaluar la primera expresión también. Esto significa que si hay filas en la tabla donde propertytype es diferente a ‘INT’, y en esas filas propertyval no son convertibles a INT, la consulta puede fallar debido a un error de conversión.
Puede hacer frente a este problema en un número de maneras. Una opción sencilla es utilizar la función TRY_CAST en lugar de CAST. Cuando la expresión de entrada no es convertible al tipo de destino, TRY_CAST retorna un NULL en lugar de fallar. La comparación de un NULL a algo produce Desconocido. Eventualmente, obtendrá el resultado correcto, sin permitir a la consulta a fallar. Así que su cláusula WHERE debe ser revisada como en la siguiente.
WHERE propertytype = 'INT' AND TRY_CAST(propertyval AS INT) > 10

Filtrando Datos Carácter

En muchos aspectos, el filtrado de datos carácter es lo mismo que el filtrado de otros tipos de datos. Esta sección cubre un par de elementos que son específicos a los datos carácter: la forma apropiada de literales y el predicado LIKE.
Un literal tiene un tipo. Si se escribe una expresión que implica operandos de diferentes tipos, SQL Server tendrá que aplicar la conversión implícita para alinear los tipos. Dependiendo de las circunstancias, las conversiones implícitas a veces pueden perjudicar el rendimiento. Es importante conocer la forma apropiada de literales de diferentes tipos y asegurarse de usar las correctas. Un ejemplo clásico para el uso de tipos literales incorrectos es con las cadenas de caracteres Unicode (tipos NCHAR y NVARCHAR). La forma correcta para un literal de cadena de caracteres Unicode es poner prefijos al literal con una N mayúscula y delimitar el literal con comillas simples; por ejemplo, N'literal'. Para un literal de cadena de caracteres regulares, solo se delimita el literal con comillas simples; por ejemplo, ‘literal’. Es un mal hábito muy típico, especificar un literal de cadena de caracteres regulares cuando la columna filtrada es de un tipo Unicode, como en el siguiente ejemplo.
SELECT empid, firstname, lastname
FROM HR.Employees
WHERE lastname = 'Davis';
Debido a que la columna y el literal tienen diferentes tipos, SQL Server convierte implícitamente un tipo de operando a otro. En este ejemplo, afortunadamente, SQL Server convierte el tipo del literal al tipo de la columna, así que aún puede confiar eficientemente en la indexación. Sin embargo, puede haber casos donde la conversión implícita perjudique el rendimiento. Es una buena práctica usar la forma adecuada, como en la siguiente.
SELECT empid, firstname, lastname
FROM HR.Employees
WHERE lastname = N'Davis';
T-SQL proporciona el predicado LIKE, que puede utilizar para filtrar los datos de cadena de caracteres (regulares y Unicode) basados en la coincidencia de patrones. La forma de un predicado utilizando LIKE es como sigue.
<column> LIKE <pattern>
El predicado LIKE soporta comodines que puede utilizar en sus patrones. La Tabla describe los comodines disponibles, su significado, y un ejemplo que muestra su uso.
Como un ejemplo, suponga que desea retornar todos los empleados cuyo apellido comienza con la letra D. Se podría utilizar la siguiente consulta.
SELECT empid, firstname, lastname
FROM HR.Employees
WHERE lastname LIKE N'D%';
Esta consulta retorna el siguiente resultado.
empid  firstname  lastname
------ ---------- -------------
1       Sara        Davis
9       Zoya        Dolgopyatova
Si desea buscar un carácter que es considerado un comodín, puede indicarle después un carácter que se designe como un carácter de escape por utilizar la palabra clave ESCAPE. Por ejemplo, la expresión col1 LIKE '!_%' ESCAPE '!' busca cadenas que comienzan con un guión bajo (_) utilizando un signo de exclamación (!) como el carácter de escape.
Rendimiento del Predicado LIKE
Cuando el patrón LIKE comienza con un prefijo conocido, por ejemplo, col LIKE 'ABC%', SQL Server puede potencialmente utilizar eficientemente un índice sobre la columna filtrada; en otras palabras, SQL Server puede confiar en el orden de los índices. Cuando el patrón comienza con un comodín, por ejemplo, col LIKE '%ABC%', SQL Server no puede confiar más en el orden de los índices. También, cuando busca una cadena que empieza con un prefijo conocido (digamos, ABC) asegúrese de utilizar el predicado LIKE, como en col LIKE 'ABC%', porque esta forma es considerada un argumento de búsqueda. Recordemos que aplicando la manipulación a la columna filtrada impide al predicado a ser un argumento de búsqueda. Por ejemplo, la forma LEFT(col, 3)='ABC' no es un argumento de búsqueda y evitará a SQL Server de poder utilizar un índice eficientemente.

Filtrando Datos Date y Time

Hay varias consideraciones importantes cuando filtra los datos Date y Time que están relacionados tanto a la exactitud de su código y a su rendimiento. Quiere pensar en cosas como la forma de expresar literales, rangos de filtro, y usar argumentos de búsqueda.
Empezará con literales. Suponga que necesita consultar la tabla Sales.Orders y retornar sólo los pedidos realizados el 12 de febrero del 2007. Se utiliza la siguiente consulta.
SELECT orderid, orderdate, empid, custid
FROM Sales.Orders
WHERE orderdate = '02/12/07';
Si es americano, esta forma probablemente signifique el 12 de febrero del 2007. Sin embargo, si es británico, esta forma probablemente le signifique el 2 de diciembre del 2007. Si es japonés, probablemente el signifique el 7 de diciembre del 2002. La pregunta es, cuando SQL Server convierte esta cadena de carácter a un tipo de fecha y de hora para alinearlo con el tipo de la columna filtrada, ¿Cómo interpreta el valor? Pues resulta, que depende del idioma del inicio de sesión que ejecuta el código. Cada inicio de sesión tiene un idioma predeterminado asociado, y el idioma predeterminado establece varias opciones de sesión sobre los inicios de sesión, incluyendo uno llamado DATEFORMAT. Un inicio de sesión con us_english tendrá el ajuste DATEFORMAT establecido a mdy, el británico a dmy y el japonés a ymd. El problema es, ¿Cómo un desarrollador expresa una fecha si quiere que sea interpretado de la manera del destinatario, sin importar quien ejecuta su código?
Hay dos enfoques principales. Una es usar una forma que es considerada independiente del idioma. Por ejemplo, la forma '20070212' es siempre interpretada como ymd, independientemente de su idioma. Note que la forma '2007-02-12' es considerada independiente del idioma sólo para los tipos de datos DATE, DATETIME2 y DATETIMEOFFSET. Lamentablemente, debido a razones históricas, esta forma es considerada dependiente del idioma para los tipos DATETIME y SMALLDATETIME. La ventaja de la forma sin los separadores es que es independiente del idioma para todos los tipos de fecha y de hora. Así que la recomendación es escribir la consulta como en la siguiente.
SELECT orderid, orderdate, empid, custid
FROM Sales.Orders
WHERE orderdate = '20070212';
Almacenar Fechas en una Columna DATETIME
La columna filtrada orderdate es de un tipo de datos DATETIME que representa tanto la fecha y la hora. Aún, el literal especificado en el filtro contiene sólo una parte de fecha. Cuando SQL Server convierte el literal al tipo de la columna filtrada, se asume la medianoche cuando una parte del tiempo no está indicada. Si quiere un filtro para retornar todas las filas de la fecha especificada, es necesario asegurarse que almacena todos los valores con la medianoche como la hora.
Otro enfoque es utilizar las funciones CONVERT o PARSE, que puede utilizar para indicar cómo quiere que SQL Server interprete el literal que especifique. La función CONVERT soporta un número de estilo representando el estilo de conversión, y la función PARSE soporta indicar un nombre de cultura.
Otro aspecto importante del filtrado de datos de fecha y de hora es tratar siempre que sea posible utilizar los argumentos de búsqueda. Por ejemplo, suponga que necesita filtrar sólo los pedidos realizados en febrero del 2007. Puede utilizar las funciones YEAR y MONTH, como en la siguiente.
SELECT orderid, orderdate, empid, custid
FROM Sales.Orders
WHERE YEAR(orderdate) = 2007 AND MONTH(orderdate) = 2;
Sin embargo, ya que aquí aplica la manipulación de la columna filtrada, el predicado no es considerado un argumento de búsqueda, y por lo tanto, SQL Server no podrá confiar en el ordenado del índice. Podría revisar su predicado como un rango, como la siguiente.
SELECT orderid, orderdate, empid, custid
FROM Sales.Orders
WHERE orderdate >= '20070201' AND orderdate < '20070301';
Ahora que no aplica la manipulación a la columna filtrada, el predicado es considerado un argumento de búsqueda, y hay la posibilidad de que SQL Server confíe en el ordenado del índice.
Si se pregunta por qué este código expresa el rango de fechas utilizando los operadores mayor o igual que (> =) y menor que (<) en lugar de utilizar BETWEEN, hay una razón para ello. Cuando utiliza BETWEEN y la columna mantiene tanto los elementos de fecha y de hora, ¿Qué utiliza como el valor final? Como puede darse cuenta, para los diferentes tipos, hay diferentes precisiones. Es más, suponga que el tipo es DATETIME, y se utiliza el siguiente predicado.
WHERE orderdate BETWEEN '20070201' AND '20070228 23:59:59.999'
La precisión del tipo es tres y un tercio de milisegundos. La parte de milisegundos del punto final 999 no es una multiplicación de la unidad de precisión, por lo que SQL Server termina redondeando el valor a medianoche del 1 de marzo del 2007. Como resultado, puede terminar obteniendo algunos pedidos que no se supone que vea. En pocas palabras, en lugar de BETWEEN, use >= y <, y esta forma funcionará correctamente en todos los casos, con todos los tipos de fecha y de hora, así la porción de tiempo sea aplicable o no.

Ejercicio 1: Usar la Cláusula WHERE para Filtrar Filas con NULLs

En este ejercicio, practica el uso de la cláusula WHERE para filtrar los pedidos no enviados de la tabla Sales.Orders.
1.    Abra el SSMS y conéctese a la base de datos de muestra TSQL2012.
2.    Se le pedirá que escriba una consulta que retorne los pedidos que no se enviaron aún. Dichos pedidos tienen un NULL en la columna shippeddate. Para su primer intento, utilice la siguiente consulta.
SELECT orderid, orderdate, custid, empid
FROM Sales.Orders
WHERE shippeddate = NULL;
Sin embargo, cuando ejecuta este código, se obtiene un conjunto de resultado vacío.
orderid      orderdate         custid      empid
----------- ----------------------- ----------- -----------
La razón para esto es que cuando la expresión compare dos NULLs, el resultado es Desconocido, y la fila es filtrada.
3.    Revisar el predicado de filtro para utilizar el operador IS NULL en lugar de la igualdad (=), como en la siguiente.
SELECT orderid, orderdate, custid, empid
FROM Sales.Orders
WHERE shippeddate IS NULL;
Esta vez, obtiene el resultado correcto, mostrado en forma abreviada.
orderid      orderdate         custid      empid
----------- ----------------------- ----------- -----------
11008       2008-04-08 00:00:00.000  20           7
11019       2008-04-13 00:00:00.000  64           6
11039       2008-04-21 00:00:00.000  47           1
...

Ejercicio 2: Usar la Cláusula WHERE para Filtrar un Rango de Fechas

En este ejercicio, practica el uso de la cláusula WHERE para filtrar los pedidos en un cierto rango de fechas de la tabla Sales.Orders.
1.    Se le pide retornar todos los pedidos que fueron colocados entre el 11 de febrero del 2008 y el 12 de febrero del 2008. La columna orderdate por la que se supone filtra es de un tipo DATETIME. Con los datos actuales en la tabla, todos los valores orderdate tienen el tiempo establecido a medianoche, pero suponga que este no es el caso, a saber, que la porción de tiempo podría ser un valor distinto de la medianoche. Para su primer intento, utilice el predicado BETWEEN, de la siguiente manera.
SELECT orderid, orderdate, custid, empid
FROM Sales.Orders
WHERE orderdate BETWEEN '20080211' AND '20080212 23:59:59.999';
Debido a que 999 no es una multiplicación de la unidad de precisión del tipo DATETIME (tres y un tercio de milisegundos), el valor final en el rango será redondeada a la próxima medianoche, y el resultado incluye filas del 13 de febrero que no solicitó.
orderid      orderdate         custid      empid
----------- ----------------------- ----------- -----------
10881        2008-02-11 00:00:00.000  12          4
10887        2008-02-13 00:00:00.000  29          8
10886        2008-02-13 00:00:00.000  34          1
10884        2008-02-12 00:00:00.000  45          4
10883        2008-02-12 00:00:00.000  48          8
10882        2008-02-11 00:00:00.000  71          4
10885        2008-02-12 00:00:00.000  76          6
2.    Para corregir el problema, revise el filtro del rango para usar los operadores >= y <, de la siguiente manera.
SELECT orderid, orderdate, custid, empid
FROM Sales.Orders
WHERE orderdate >= '20080211' AND orderdate < '20080213';

Esta vez, se obtiene el resultado correcto.

1 comentario: