2009-08-20 63 views
7

Me pregunto si hay una consulta de buen rendimiento para seleccionar fechas distintas (ignorando los tiempos) de una tabla con un campo de fecha y hora en SQL Server.Cómo seleccionar rápidamente las fechas DISTINCT de un campo Fecha/Hora, SQL Server

Mi problema no es conseguir que el servidor realmente lo haga (ya he visto this question, y ya teníamos algo similar utilizando DISTINCT). El problema es si hay algún truco para hacerlo más rápidamente. Con los datos que estamos usando, nuestra consulta actual está devolviendo ~ 80 días distintos para los cuales hay ~ 40,000 filas de datos (después de filtrar en otra columna indexada), hay un índice en la columna de fecha, y la consulta siempre se las arregla para tomar 5+ segundos. Que es muy lento

Cambiar la estructura de la base de datos puede ser una opción, pero menos deseable.

Respuesta

6

Cada opción que involucra la manipulación CAST o TRUNCATE o DATEPART en el campo de fecha y hora tiene el mismo problema: la consulta debe escanear todo el conjunto de resultados (los 40k) para encontrar las fechas distintas. El rendimiento puede variar marginalmente entre varias implementaciones.

Lo que realmente necesita es tener un índice que pueda producir la respuesta en un abrir y cerrar de ojos. Puede tener una columna calculada persistente e indexar (requiere cambios en la estructura de la tabla) o una vista indizada (requires Enterprise Edition for QO to consider the index out-of-the-box).

persistentes de columna calculada:

alter table foo add date_only as convert(char(8), [datetimecolumn], 112) persisted; 
create index idx_foo_date_only on foo(date_only); 

vista indizada:

create view v_foo_with_date_only 
with schemabinding as 
select id 
    , convert(char(8), [datetimecolumn], 112) as date_only 
from dbo.foo; 
create unique clustered index idx_v_foo on v_foo_with_date_only(date_only, id); 

actualización

para eliminar por completo la exploración podría utilizar un GROUP BY engañado vista indizada, como este:

create view v_foo_with_date_only 
with schemabinding as 
select 
    convert(char(8), [d], 112) as date_only 
    , count_big(*) as [dummy] 
from dbo.foo 
group by convert(char(8), [d], 112) 

create unique clustered index idx_v_foo on v_foo_with_date_only(date_only) 

En su lugar, la consulta select distinct date_only from foo utilizará esta vista indizada. Sigue siendo un escaneo técnico, pero en un índice ya 'distinto', por lo que solo se escanean los registros necesarios. Es un truco, creo, no lo recomendaría para el código de producción en vivo.

AFAIK SQL Server no tiene la capacidad de escanear un índice verdadero con omisión de repeticiones, es decir. busca la cima, luego busca más que la cima, luego busca sucesivamente más que el último encontrado.

+0

¿Hay alguna forma de utilizar 'SKIP SCAN' en' SQL Server'? Acabo de probar tu solución en una tabla '2M' y empeoró (' DISTINCT CAST (...) 'en un campo' DATETIME' tomó '850 ms' con' Hash Match Aggregate', 'DISTINCT date' took '1800 ms' con un' Agregado de flujo'). 'Oracle' y' MySQL' saltaban sobre los distintos campos en el índice, 'SQL Server' no lo hace. – Quassnoi

+0

Debe seleccionar date_only distinto después de que se haya creado un índice en él. –

+0

'@ Remus': Creé un índice, y el optimizador lo usó. – Quassnoi

9

He utilizado el siguiente:

CAST(FLOOR(CAST(@date as FLOAT)) as DateTime); 

Esto elimina el tiempo desde la fecha mediante la conversión a un float y el truncamiento de la parte "tiempo", que es el decimal de la float.

Parece un poco torpe pero funciona bien en un gran conjunto de datos (~ 100,000 filas) que utilizo repetidamente a lo largo del día.

3

La forma más sencilla es agregar una columna calculada solo para la parte de la fecha y seleccionarla. Podría hacer esto en una vista si no quiere cambiar la tabla.

2

Actualización:

solución por debajo de la prueba de la eficiencia en una mesa y toma 2M pero 40 ms.

Normal DISTINCT en una columna calculada indexada tomó 9 seconds.

Ver esta entrada en mi blog para los detalles de rendimiento:


Desafortunadamente, SQL Server s INDEX FOR GROUP-BY 's optimizador puede hacer ni MySQL de SKIP SCAN Oracle ni'.

Siempre es Stream Aggregate que toma mucho tiempo.

Se puede construir una lista de posibles fechas utilizando un recursivo CTE y unirlo con su tabla:

WITH rows AS (
     SELECT CAST(CAST(CAST(MIN(date) AS FLOAT) AS INTEGER) AS DATETIME) AS mindate, MAX(date) AS maxdate 
     FROM mytable 
     UNION ALL 
     SELECT mindate + 1, maxdate 
     FROM rows 
     WHERE mindate < maxdate 
     ) 
SELECT mindate 
FROM rows 
WHERE EXISTS 
     (
     SELECT NULL 
     FROM mytable 
     WHERE date >= mindate 
       AND date < mindate + 1 
     ) 
OPTION (MAXRECURSION 0) 

Esto será más eficiente que Stream Aggregate

+0

La construcción de una tabla de fechas y luego semi-unirse a la original es una gran solución. En mi humilde opinión, la sobrecarga adicional de una columna persistente con un índice o una vista indexada solo tiene sentido si tiene que hacer esta operación con mucha frecuencia (conjetura arbitraria: como un par de cientos de veces al día). Siempre preferiría primero intentar encontrar una mejor consulta que agregar más complejidad/sobrecarga a la estructura de la base de datos. –

0

Si se quiere evitar la extracción de paso o reformateando la fecha, que presumiblemente es la causa principal de la demora (al forzar un escaneo completo de la tabla), no tiene otra alternativa que almacenar la fecha solo como parte de la fecha y hora, lo que lamentablemente requerirá una alteración de la estructura de la base de datos.

Si su uso de SQL Server 2005 o posterior a continuación, un campo calculado PERSISTED es el camino a seguir

 
Unless otherwise specified, computed columns are virtual columns that are 
not physically stored in the table. Their values are recalculated every 
time they are referenced in a query. The Database Engine uses the PERSISTED 
keyword in the CREATE TABLE and ALTER TABLE statements to physically store 
computed columns in the table. Their values are updated when any columns 
that are part of their calculation change. By marking a computed column as 
PERSISTED, you can create an index on a computed column that is deterministic 
but not precise. 
+1

La causa principal del retraso es el escaneo y la ordenación para producir los distintos. A menos que ocurra algo * extremadamente * complejo en una operación escalar, las demoras en una base de datos siempre están relacionadas con el acceso a los datos, no con las operaciones escalares. –

+0

Es la causa principal de la demora porque obliga a una exploración completa de la tabla. Lo siento, debería haber dejado claro – Cruachan

0

¿Cuál es su predicado en esa otra columna filtrada? ¿Has probado si obtienes una mejora de un índice en esa otra columna filtrada, seguido del campo de fecha y hora?

Estoy adivinando aquí, pero 5 segundos para filtrar un conjunto de tal vez 100000 filas hasta 40000 y luego hacer una ordenación (que es lo que pasa probablemente) no me parece un momento irrazonable. ¿Por qué dices que es demasiado lento? Porque no coincide con las expectativas?

3

No estoy seguro de por qué su consulta existente tomaría más de 5 segundos para 40,000 filas.

Acabo de probar la siguiente consulta en una tabla con 100.000 filas y me devolvió en menos de 0.1s.

SELECT DISTINCT DATEADD(day, 0, DATEDIFF(day, 0, your_date_column)) 
FROM your_table 

(Tenga en cuenta que esta consulta probablemente no será capaz de tomar ventaja de todos los índices de la columna de la fecha, pero debe ser razonablemente rápido, en el supuesto de que no está ejecutando docenas de veces por segundo.)

+0

fácil y limpio, esta debería ser la respuesta correcta –

0

Sólo convertir la fecha: dateadd(dd,0, datediff(dd,0,[Some_Column]))

1

utilicé esta

SELECT 
DISTINCT DATE_FORMAT(your_date_column,'%Y-%m-%d') AS date 
FROM ... 
+0

No estoy seguro eficiencia, pero definitivamente es la manera más bonita de hacerlo. – ylnor

5

Esto funciona para mí:

SELECT distinct(CONVERT(varchar(10), {your date column}, 111)) 
FROM {your table name} 
Cuestiones relacionadas