2012-02-16 17 views
5

Tengo una tabla que contiene una gran cantidad de datos, donde nos preocupa especialmente el campo date. La razón de esto es que el volumen de datos simplemente subió ~ 30x, y las viejas formas se desmoronarán pronto. La consulta espero que me puede ayudar a optimizar las necesidades a:Intentando optimizar una consulta que selecciona un "registro aproximado aproximado"

  • tomar una lista de fechas (generada por una función con valores de tabla basada CTE)
  • recuperar un registro único para cada una de esas fechas
    • basado en una definición de 'más cercana'

Por ejemplo, la tabla actual contiene datos de cada 5 segundos (+/- un poco) intervalos. Necesito muestrear esa tabla y obtener el registro que más se acerca a un intervalo de 30 segundos.

Lo que tengo ahora funciona bien. Simplemente tengo curiosidad si hay una forma de optimizarlo más. Si puedo hacerlo en Linq To SQL, eso también sería genial. Incluso estoy interesado en sugerencias sobre índices, dada la cantidad de valores de fecha (~ 2 millones de filas mínimas).

declare @st datetime ; set @st = '2012-01-31 05:05:00'; 
declare @end datetime ; set @end = '2012-01-31 05:10:00'; 

select distinct 
    log.* -- id, 
from 
    dbo.fn_GenerateDateSteps(@st, @end, 30) as d 
     inner join lotsOfLogData log on l.Id = (
      select top 1 e.[Id] 
      from 
       lotsOfLogData as log -- contains data in 5 second intervals 
      where 
       log.stationId = 1000 
       -- search for dates in a certain range 
       AND utcTime between DateAdd(s, -10, dt) AND DateAdd(s, 5, dt) 
      order by 
       -- get the 'closest'. this can change a little, but will always 
       -- be based on a difference between the date 
       abs(datediff(s, dt, UtcTime)) 
     ) 
    -- updated the query to be correct. stadionId should be inside the subquery 

La estructura de la tabla de lotsOfLogData está debajo. Hay relativamente pocos ID de estación (quizás 50), pero muchos registros para cada uno. Conocemos la identificación de la estación cuando consultamos.

create table ##lotsOfLogData (
    Id   bigint  identity(1,1) not null 
, StationId int   not null 
, UtcTime  datetime not null 
    -- 20 other fields, used for other calculations 
) 

fn_GenerateDateSteps devuelve un conjunto de datos como éste, para los parámetros dados:

[DT] 
2012-01-31 05:05:00.000 
2012-01-31 05:05:30.000 
2012-01-31 05:06:00.000 
2012-01-31 05:06:30.000 (and so on, every 30 seconds) 

me han hecho esto con una tabla temporal, así, de esta manera, sino que salió sólo un poco un poco más caro.

declare @dates table (dt datetime, ClosestId bigint); 
insert into @dates (dt) select dt from dbo.fn_GenerateDateSteps(@st, @end, 30) 
update @dates set closestId = (-- same subquery as above) 
select * from lotsOfLogData inner join @dates on Id = ClosestId 

Editar: se ha corregido hasta

Got 200K + filas para trabajar ahora. Lo intenté en ambos sentidos, y la cruz se aplica con un índice apropiado (id/time + includes (... todas las columnas ...) funcionó bien. Sin embargo, terminé con la consulta que comencé, usando un más simple (y existente) . índice de [id + tiempo] La consulta más ampliamente comprensible por qué es que se establecieron en que uno Tal vez todavía hay una mejor manera de hacerlo, pero no puedo verlo:. D

-- subtree cost (crossapply) : .0808 
-- subtree cost (id based) : .0797 

-- see above query for what i ended up with 

Respuesta

1

Usted podría intentar

  • cambiar el inner join a un cross apply.
  • Mueva where log.stationid a la subselección.

instrucción SQL

SELECT DISTINCT log.* -- id, 
FROM dbo.fn_GenerateDateSteps(@st, @end, 30) AS d 
     CROSS APPLY (
      SELECT TOP 1 log.* 
      FROM lotsOfLogData AS log -- contains data in 5 second intervals 
      WHERE -- search for dates in a certain range 
        utcTime between DATEADD(s, -10, d.dt) AND DATEADD(s, 5, d.dt) 
        AND log.stationid = 1000 
      ORDER BY 
        -- get the 'closest'. this can change a little, but will always 
        -- be based on a difference between the date 
        ABS(DATEDIFF(s, d.dt, UtcTime)) 
     ) log 
+0

La aplicación cruzada quiere que haga un índice en la estaciónid/hora que también incluye * todos * los otros datos en la tabla. Sin el índice, funciona exactamente igual que la consulta simple, por lo que en este caso la cruz no va a funcionar :) Sin embargo, ni siquiera lo sabía, ¡así que gracias! –

+0

Ah, y tuve un error en esa consulta;) * Estoy * obligado * a poner el ID de la estación en la subconsulta porque de lo contrario coincidiré con cualquier ID de la estación que esté dentro del rango. Después de hacer eso, se usa el índice adecuado y todo es súper rápido (ish) –

+0

@AndrewBacker: ausente, pero gracias por mantenernos informados. –

1

Sólo algunos pensamientos ... realmente no llamaría esto una respuesta, pero era demasiado grande para el cuadro de comentarios.

Antes que nada, consultaría el plan de ejecución para la consulta si aún no lo ha hecho.

Más esotérico: ¿tiene la opción de representar fechas como valores primitivos (como un número entero que representa segundos/minutos desde un tiempo bien definido)? Aunque creo que SQL Server almacena las fechas como valores numéricos bajo el capó, las operaciones en una primitiva podrían ser un poco más rápidas porque eliminaría las llamadas repetidas a DateAdd() y DateDiff().

This (fairly old) article da ejemplos de cómo SQL Server realmente almacena las fechas. Quizás podría dejar sus fechas como DATETIME pero operar con matemáticas básicas.

Independientemente del tipo de datos, experimentaría con un índice agrupado en la columna de fecha, ya que parece que sus búsquedas podrían beneficiarse del ordenamiento físico que proporciona un índice agrupado, especialmente si está buscando dentro de rangos ajustados. De nuevo, el plan de ejecución probablemente sería esclarecedor.

También pude ver un esquema en estrella que se usa para representar sus datos, con una dimensión de fecha que contenía generalizaciones de fecha. A continuación, puede buscar contra las generalizaciones. Incluso si no se utilizaran las generalizaciones, el número real de fechas se reduciría porque todos los hechos con la misma fecha podrían apuntar al mismo registro en la dimensión, por lo que la fecha solo tendría que evaluarse una vez.

Por último, ¿qué sugiere el asistente de ajuste de rendimiento de SQL (creo que es en 2005, sé que es en 2008) para su consulta? No recomendaría implementar ciegamente sus sugerencias, pero a menudo encuentro buenas ideas en las cosas que recomienda.

+0

Por desgracia, no puede hacer mucho con el formato de datos. Un servicio externo recibe los datos y los registra allí para nosotros. Miré el plan de ejecución, pero es enorme =) Tengo un índice agrupado en la identificación de la estación, y un estándar. índice en la fecha. Simplemente no tengo suficientes datos realistas para probarlo todavía, y con un conjunto lo suficientemente pequeño no parece importar si hay un índice o no –

Cuestiones relacionadas