2010-11-01 16 views
12

Tengo dos tablas en SQL y necesito poder hacer una combinación basada en la marca de tiempo en la tabla B anterior o igual a la marca de tiempo en la tabla A.Consulta SQL para unir dos tablas basadas en la marca de tiempo más cercana

lo tanto, aquí hay algunos datos falsos para dos mesas y la salida deseada:

Casos

cerrados (Tabla a)

 
| id | resolution |   timestamp   | 
------------------------------------------------ 
| 1 |  solved | 2006-10-05 11:55:44.888153 | 
| 2 |  closed | 2007-10-07 12:34:17.033498 | 
| 3 | trashed | 2008-10-09 08:19:36.983747 | 
| 4 |  solved | 2010-10-13 04:28:14.348753 | 

Clasificación (Tabla B)

 

| id | value |   timestamp   | 
------------------------------------------------- 
| 1 | freshman | 2006-01-01 12:02:44.888153 | 
| 2 | sophomore | 2007-01-01 12:01:19.984333 | 
| 3 |  junior | 2008-01-01 12:02:28.746149 | 

resultados deseados

 
| id | resolution |   timestamp   | value | 
-------------------------------------------------------------- 
| 1 |  solved | 2006-10-05 11:55:44.888153 | freshman | 
| 2 |  closed | 2007-10-07 12:34:17.033498 | sophomore | 
| 3 | trashed | 2008-10-09 08:19:36.983747 |  junior | 
| 4 |  solved | 2010-10-13 04:28:14.348753 |  junior | 

Por lo tanto, sé que el código debe ser similar a la siguiente, simplemente no puedo averiguar qué hacer con la parte ON del JOIN ($ 1 y $ 2 son las variables que serán pasados ​​en):

SELECT case.id, case.resolution, case.timestamp, class.value 
    FROM closed_cases AS case 
    LEFT JOIN classifications AS class ON ??? 
    WHERE case.timestamp BETWEEN $1 AND $2; 

sé que podría utilizar un sub-select, pero esto estará operando en al le hasta unos miles de filas, probablemente más, y necesito que sea realmente rápido; así que esperaba una cláusula simple que pudiera hacerlo.

+0

Creo que necesita su sub-select. ¿Has probado el rendimiento y lo has encontrado inaceptable? – Beth

+0

si la versión de SQL que está utilizando admite funciones analíticas de ventanas, debería poder hacerlo sin una selección secundaria, pero algunas versiones de SQL no las admiten. Para una sub selección única en miles de filas, el rendimiento no debería ser tan malo. (La sub-selección estará en la tabla de clasificación, ¿realmente tendrá más de unos miles de filas?) –

+0

@Mark - En realidad, ahora que lo pienso, la tabla de clasificación debería tener menos filas que esa, ya que verificar si los datos realmente han cambiado desde la versión más reciente.Así que supongo que la selección secundaria habría funcionado bien, pero creo que agregar el tiempo de finalización es una solución mucho más limpia. –

Respuesta

7

Si puede hacer cambios en las estructuras de la tabla, le recomiendo cambiar la tabla de clasificación para incluir una fecha de finalización así como una fecha de inicio; será mucho más fácil unirse a la tabla de esa manera.

Si no es así, le sugiero lo siguiente:

SELECT case.id, case.resolution, case.timestamp, class.value 
    FROM closed_cases AS case 
    LEFT JOIN (select c.*, 
        (select min(timestamp) 
        from classifications c1 
         where c1.timestamp > c.timestamp) timeend 
      from classifications c) AS class 
    ON case.timestamp >= class.timestamp and 
    (case.timestamp < class.timeend or class.timeend IS NULL) 
    WHERE case.timestamp BETWEEN $1 AND $2; 

EDITAR - con la fecha de finalización de la clasificación:

SELECT case.id, case.resolution, case.timestamp, class.value 
    FROM closed_cases AS case 
    LEFT JOIN classifications AS class 
    ON case.timestamp >= class.timestamp and case.timestamp < class.timeend 
    WHERE case.timestamp BETWEEN $1 AND $2; 
+0

¿Puede explicarnos brevemente cómo sería útil cambiar la estructura de la tabla para incluir la fecha de finalización? En teoría, podría hacerlo estableciendo siempre que la fecha de finalización actual es muy lejana en el futuro y actualizando la fecha de finalización de la entrada anterior para que sea la fecha de inicio de las entradas actuales. –

+0

@Topher - consulta adicional agregada; no se requiere selección secundaria, y la consulta debe ser sargable. –

+0

+ 1/Aceptado - Gracias por la ayuda ¡Marque! Añadiré el tiempo de finalización para facilitar la vida. –

0

cambie la marca de tiempo y use un int como clave para conectar las tablas. Esto funciona mucho más rápido que la comparación de la fecha

tabla 1 campo1 campo2 field3 ConnectorField

tabla2 campo1 campo2 field3 ConnectorField

y todo lo que hay que hacer es SELECT * FROM table1 T1 inner join table2 T2 en T1.ConnectorField = T2.ConnectorField

+0

Esta solución requiere que el OP pueda cambiar la estructura de la base de datos existente y evitará cambios en los rangos de fechas de las clasificaciones con respecto a los casos existentes. –

+0

No importa si utilizo un número entero (no son marcas de tiempo almacenadas como enteros de todos modos), esto aún no resuelve el problema de "más cercano a". Todavía necesitaría hacer la unión basada en exactamente un entero en la tabla B que sea menor o igual que el número entero de la tabla A. –

+0

@Topher, probablemente no entendí la especificación. – none

-1
SELECT case.id, case.resolution, case.timestamp, class.value 
    FROM closed_cases AS case 
    LEFT JOIN classifications AS class 
    ON case.timestamp >= class.timestamp 
    WHERE case.timestamp BETWEEN $1 AND $2; 
+0

Esto devolverá todas las clasificaciones después de la marca de tiempo del caso para cada caso, en lugar de solo la clasificación aplicable, por lo que para el ejemplo proporcionado, verá 11 filas devueltas en lugar de las 4 requeridas. –

+0

@Mark Mannister - Exactamente. Solo necesito las 4 filas (voy a hacer un conteo y agruparlas más tarde). –

Cuestiones relacionadas