2008-11-12 6 views
12

Esto es algo que aparece con tanta frecuencia que casi dejo de pensarlo, pero estoy casi seguro de que no lo estoy haciendo de la mejor manera.La mejor manera de seleccionar la fila con la marca de tiempo más reciente que coincida con un criterio

La pregunta: Supongamos que tenemos la siguiente tabla

CREATE TABLE TEST_TABLE 
(
    ID   INTEGER, 
    TEST_VALUE NUMBER, 
    UPDATED  DATE, 
    FOREIGN_KEY INTEGER 
); 

¿Cuál es la mejor manera de seleccionar el TEST_VALUE asociado con la fila más recientemente actualizado, en caso FOREIGN_KEY = 10?

EDIT: Vamos a hacer esto más interesante ya que las respuestas a continuación simplemente van con mi método de clasificación y luego seleccionando la fila superior. No está mal, pero para grandes retornos, el orden por mataría el rendimiento. Por lo tanto, puntos de bonificación: cómo hacerlo de una manera escalable (es decir, sin el orden innecesario por).

+0

El orden por no mataría el rendimiento si hubiera un índice en el lugar que podría aprovecharse –

Respuesta

13

Las funciones analíticas son sus amigos

SQL> select * from test_table; 

     ID TEST_VALUE UPDATED FOREIGN_KEY 
---------- ---------- --------- ----------- 
     1   10 12-NOV-08   10 
     2   20 11-NOV-08   10 

SQL> ed 
Wrote file afiedt.buf 

    1* select * from test_table 
SQL> ed 
Wrote file afiedt.buf 

    1 select max(test_value) keep (dense_rank last order by updated) 
    2 from test_table 
    3* where foreign_key = 10 
SQL>/

MAX(TEST_VALUE)KEEP(DENSE_RANKLASTORDERBYUPDATED) 
------------------------------------------------- 
               10 

También puede extender el proceso a obtener la información para toda la fila

SQL> ed 
Wrote file afiedt.buf 

    1 select max(id) keep (dense_rank last order by updated) id, 
    2   max(test_value) keep (dense_rank last order by updated) test_value 
, 
    3   max(updated) keep (dense_rank last order by updated) updated 
    4 from test_table 
    5* where foreign_key = 10 
SQL>/

     ID TEST_VALUE UPDATED 
---------- ---------- --------- 
     1   10 12-NOV-08 

Y los enfoques analíticos generalmente son bastante eficaces.

También debo señalar que las funciones analíticas son relativamente nuevas, por lo que si tiene algo anterior a 9.0.1, es posible que esto no funcione. Ya no se trata de una gran población, pero siempre hay algunas personas atrapadas en versiones antiguas.

+0

que es un poco loco consultar a mi amigo, buen trabajo –

+0

¿No sería mejor que mi consulta rownum a continuación? Los análisis de acuerdo son una mejor solución genérica. –

+0

No estoy seguro de cómo funcionan realmente las analíticas, pero las CS básicas le dirían que el tiempo de ejecución óptimo para esta tarea la consulta debería ser O (n) donde n es el número de filas que coinciden con where. Con un pedido por este sería O (n^2) –

1

La forma probablemente inferior que actualmente hago para hacer algo como esto es

SELECT TEST_VALUE 
FROM TEST_TABLE 
WHERE ID = (
    SELECT ID 
    FROM (
    SELECT ID 
    FROM TEST_TABLE 
    WHERE FOREIGN_KEY = 10 
    ORDER BY UPDATED DESC 
) 
    WHERE ROWNUM = 1 
) 

pero por favor StackOverflow Genios, me enseñe algunos trucos

3

O bien utilizar un sub-consulta

WHERE updated = (SELECT MAX(updated) ...) 

o seleccione el registro TOP 1 con

ORDER BY updated DESC 

En la sintaxis de Oracle esto sería:

SELECT 
    * 
FROM 
(
    SELECT * FROM test_table 
    ORDER BY updated DESC 
) 
WHERE 
    ROWNUM = 1 
+0

Más de un registro con diferentes FOREIGN_KEY s se pueden actualizar al mismo tiempo aunque ... –

+0

No soy de la gente de Oracle , como habrás adivinado por mi elección de sintaxis. :-) Pero el concepto general de seleccionar el registro TOP 1 debe trascender los límites de sintaxis. – Tomalak

+0

Sí, eso parece ser lo que la gente está insinuando, pero para grandes retornos la necesidad de ordenar podría matar la pretensión –

-1
select test_value 
from 
(
    select test_value 
    from test_table 
    where foreign_key=10 
    order by updated desc 
) 
where rownum = 1 

Oracle es lo suficientemente inteligente como para darse cuenta de que sólo necesita una única fila de la selecta interior y que va a hacer esto de manera eficiente.

-1

¿no este trabajo:

SELECT TOP 1 ID 
FROM test_table 
WHERE FOREIGN_KEY = 10 
ORDER BY UPDATED DESC 

hay necesidad de una subconsulta ...

+0

Ninguna cláusula TOP en Oracle ... – Tomalak

+0

también necesitaría una subconsulta para seleccionar test_value –

+0

Oh. No estaba enterado de eso. Mis disculpas :) Supongo que estoy demasiado acostumbrado a MS SQL – TJMonk15

2

En primer lugar, siempre tendrá que mirar todas las filas con esa clave externa, y encontrar la que tenga el valor ACTUALIZADO más alto ... lo que significa un MAX u ORDER BY. La eficacia de la comparación depende en parte del optimizador, por lo que dependerá de su versión de Oracle. Sin embargo, sus estructuras de datos pueden tener un mayor impacto en el rendimiento real. Un índice en FOREIGN_KEY, DESC ACTUALIZADO, TEST_VALUE probablemente proporcione la solución más escalable para realizar consultas ya que Oracle normalmente podrá dar la respuesta que acaba de acceder a un bloque de hoja individual.Puede haber un impacto perjudicial en las inserciones, ya que se deben insertar nuevos registros en esa estructura.

+0

sí, pero Max es solo O (n) mientras que el orden por es mayor –

0

El rendimiento dependerá de lo indexado. Aquí hay un método.

WITH 
ten AS 
(
    SELECT * 
    FROM TEST_TABLE 
    WHERE FOREIGH_KEY = 10 
) 
SELECT TEST_VALUE 
FROM ten 
WHERE UPDATED = 
(
    SELECT MAX(DATE) 
    FROM ten 
) 
1
SELECT TEST_VALUE 
    FROM TEST_TABLE 
WHERE UPDATED  = (SELECT MAX(UPDATED) 
          FROM TEST_TABLE 
         WHERE FOREIGN_KEY = 10) 
    AND FOREIGN-KEY = 10 
    AND ROWNUM  = 1 -- Just in case records have the same UPDATED date 

Más bien que dar el primer registro que podría romper un empate con el identificador más alto o tal vez menos/TEST_VALUE más grande

Un índice de FOREIGN_KEY, ACTUALIZADO ayudaría a realizar consultas de rendimiento.

1

Hasta que leí la respuesta de Justin Cave, he usado el siguiente patrón para obtener los registros más recientes en masa.

WITH test_table_ranked AS (
    SELECT 
     test_table.*, 
     ROW_NUMBER() OVER (
      PARTITION BY foreign_key ORDER BY updated DESC 
     ) AS most_recent 
    FROM 
     test_table 
) 
SELECT * 
FROM test_table_ranked 
WHERE most_recent = 1 
-- AND foreign_key = 10 

Esta consulta encuentra las actualizaciones más recientes para cada clave foránea en la tabla. Aunque la respuesta de Justin es más rápida cuando se conoce la clave, esta consulta también funciona en SQL Server.

Cuestiones relacionadas