Лучший способ выбрать строку с самой последней отметкой времени, которая соответствует критерию - PullRequest
12 голосов
/ 13 ноября 2008

Это происходит так часто, что я почти перестал думать об этом, но я почти уверен, что я делаю это не лучшим образом.

Вопрос: предположим, у вас есть следующая таблица

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

Как лучше всего выбрать TEST_VALUE, связанный с последней обновленной строкой, где FOREIGN_KEY = 10?

РЕДАКТИРОВАТЬ: Давайте сделаем это более интересным, так как ответы ниже просто следуют моему методу сортировки и затем выбирают верхнюю строку. Неплохо, но для больших возвратов порядок убил бы производительность. Итак, бонусные баллы: как сделать это в масштабируемой форме (т.е. без ненужного заказа по.)

Ответы [ 10 ]

13 голосов
/ 13 ноября 2008

Аналитические функции ваших друзей

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

Вы также можете расширить это, чтобы получить информацию для всей строки

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

А аналитические подходы вообще чертовски эффективны.

Я также должен отметить, что аналитические функции являются относительно новыми, поэтому, если вы работаете с чем-то более ранним, чем 9.0.1, это может не сработать. Это уже не огромная популяция, но всегда есть несколько людей, которые застряли на старых версиях.

3 голосов
/ 13 ноября 2008

Либо используйте подзапрос

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

или выберите ТОП 1 с помощью

ORDER BY updated DESC

В синтаксисе Oracle это будет:

SELECT 
  * 
FROM 
(
  SELECT * FROM test_table
  ORDER BY updated DESC
)
WHERE 
  ROWNUM = 1
2 голосов
/ 13 ноября 2008

Во-первых, вам всегда нужно будет просмотреть все строки с этим внешним ключом и найти ту, которая имеет наибольшее значение UPDATED ... что означает MAX или ORDER BY. Эффективность сравнения частично зависит от оптимизатора, поэтому будет зависеть от вашей версии Oracle. Ваши структуры данных могут оказать большее влияние на фактическую производительность. Индекс FOREIGN_KEY, UPDATED DESC, TEST_VALUE, вероятно, даст наиболее масштабируемое решение для запросов, поскольку Oracle обычно может дать ответ, просто получив доступ к одному листовому блоку. Это может иметь пагубные последствия для вставок, поскольку в эту структуру должны быть вставлены новые записи.

1 голос
/ 06 февраля 2015

До тех пор, пока я не прочитал ответ Джастина Кейва, я использовал следующую схему, чтобы получить самые последние записи в массовом порядке.

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

Этот запрос находит самые последние обновления для каждого внешнего ключа в таблице. Хотя ответ Джастина быстрее, когда известен ключ, этот запрос также работает в SQL Server.

1 голос
/ 13 ноября 2008
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

Вместо первой записи вы можете разорвать связь с самым высоким идентификатором или, возможно, с наименьшим / наибольшим значением TEST_VALUE.

Индекс FOREIGN_KEY, UPDATED, поможет повысить производительность запроса.

1 голос
/ 13 ноября 2008

Вероятно, худший способ, которым я сейчас занимаюсь, делает что-то вроде этого:

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
)

но, пожалуйста, StackOverflow Geniuses, научите меня некоторым трюкам

0 голосов
/ 13 ноября 2008

Здесь есть Oracle SQL FAQ, который может вам помочь:

http://www.orafaq.com/wiki/SQL_FAQ

0 голосов
/ 13 ноября 2008

Производительность будет зависеть от того, что индексируется. Вот метод.

WITH 
ten AS
(
    SELECT *
    FROM TEST_TABLE
    WHERE FOREIGH_KEY = 10
)
SELECT TEST_VALUE 
FROM ten
WHERE UPDATED = 
(
    SELECT MAX(DATE)
    FROM ten
)
0 голосов
/ 13 ноября 2008

это не сработает:

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

нет необходимости в подзапросе ...

0 голосов
/ 13 ноября 2008
select test_value
from
(
  select test_value 
  from test_table
  where foreign_key=10
  order by updated desc
)
where rownum = 1

Oracle достаточно умен, чтобы понять, что ему нужна только одна строка из внутреннего выбора, и он сделает это эффективно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...