PostgreSQL - получить строку с максимальным значением для столбца

Объект Assignment имеет свойство ( Resource ), которое возвращает связанный объект Resource , что делает эту задачу тривиальной:

For Each T In ActiveProject.Tasks

For Each asn In T.Assignments
    If asn.ResourceName = "John" Then  'Find the User Resources

     ' print resource's initials and standard rate 
     Debug.Print asn.Resource.Initials, asn.Resource.StandardRate

    End If
Next asn
Next T

87
задан Community 23 May 2017 в 10:31
поделиться

5 ответов

На таблице с 158k псевдослучайными строками (usr_id равномерно распределенный между 0 и 10k, trans_id равномерно распределенный между 0 и 30),

стоимостью запроса, ниже, я обращаюсь к оценке затрат основанного на стоимости оптимизатора Пост-ГРЭС (со значением по умолчанию Пост-ГРЭС xxx_cost значения), который является взвешенной функциональной оценкой необходимого ввода-вывода и ресурсов ЦП; можно получить, это путем разжигания PgAdminIII и выполнения "Запрашивает/Объясняет (F7)" на запросе с, "Запрашивают/Объясняют опции" набор, чтобы "Проанализировать"

  • , запрос Quassnoy имеет оценку затрат 745k (!) и завершается за 1,3 секунды (учитывая составной индекс на (usr_id, trans_id, time_stamp))
  • , запрос счета имеет оценку затрат 93k и завершается за 2,9 секунды (учитывая составной индекс на (usr_id, trans_id))
  • , Запрос № 1 ниже имеет оценку затрат 16k и завершается в 800 мс (учитывая составной индекс на (usr_id, trans_id, time_stamp))
  • , Запрос № 2 ниже имеет оценку затрат 14k и завершается в 800 мс (учитывая составной функциональный индекс на (usr_id, EXTRACT(EPOCH FROM time_stamp), trans_id))
    • это Определенное для пост-ГРЭС
  • , Запрос № 3 ниже (Пост-ГРЭС 8.4 +) имеет время оценки затрат и завершения, сопоставимое с (или лучше, чем) запрос № 2 (учитывая составной индекс на (usr_id, time_stamp, trans_id)); это имеет преимущество сканирования lives таблица только однажды и, должны Вы временно увеличиваться (в случае необходимости) work_mem для размещения вида в памяти, это будет безусловно самым быстрым из всех запросов.

Все случаи выше включают извлечение полного 10k набора результатов строк.

Вашей целью является минимальная оценка затрат и минимальное время выполнения запросов с акцентом на ориентировочную стоимость. Выполнение запросов значительно может зависимый на условиях во время выполнения (например, кэшируются ли соответствующие строки уже полностью в памяти или не), тогда как оценка затрат не. С другой стороны, имейте в виду, что оценка затрат - точно это, оценка.

лучшее время выполнения запросов получено при работе специализированной базы данных без загрузки (например, играя с pgAdminIII на ПК разработки.) Время запроса будет варьироваться по производству на основе фактического распространения загрузки/доступа к данным машины. Когда один запрос кажется немного быстрее (< 20%), чем другой, но имеет очень более высокая стоимость, обычно будет более мудро выбрать то с более высоким временем выполнения, но более низкой ценой.

, Когда Вы ожидаете, что не будет никакой борьбы за память на Вашей производственной машине в то время, когда запрос выполняется (например, кэш RDBMS, и кэш файловой системы не будет перегружен параллельными запросами и/или действием файловой системы), тогда время запроса, которое Вы получили в автономном (например, pgAdminIII на ПК разработки), режим будет представительным. Если будет конкуренция в производственной системе, то время запроса ухудшится пропорционально к отношению ориентировочной стоимости, поскольку запрос с более низкой ценой не полагается так же на кэш , тогда как запрос с более высокой стоимостью пересмотрит те же данные много раз (инициировавший дополнительный ввод-вывод в отсутствие стабильного кэша), например:

              cost | time (dedicated machine) |     time (under load) |
-------------------+--------------------------+-----------------------+
some query A:   5k | (all data cached)  900ms | (less i/o)     1000ms |
some query B:  50k | (all data cached)  900ms | (lots of i/o) 10000ms |

не забывают работать ANALYZE lives однажды за созданием необходимых индексов.

<час>

обновление Запроса Запроса № 2

-- cheat to obtain a max of the (time_stamp, trans_id) tuple in one pass
-- this results in a single table scan and one nested index lookup into lives,
--  by far the least I/O intensive operation even in case of great scarcity
--  of memory (least reliant on cache for the best performance)
SELECT
  l1.*
 FROM
  lives AS l1
 INNER JOIN (
   SELECT
     usr_id,
     MAX(ARRAY[EXTRACT(EPOCH FROM time_stamp),trans_id])
       AS compound_time_stamp
    FROM
     lives
    GROUP BY
     usr_id
  ) AS l2
ON
  l1.usr_id = l2.usr_id AND
  EXTRACT(EPOCH FROM l1.time_stamp) = l2.compound_time_stamp[1] AND
  l1.trans_id = l2.compound_time_stamp[2]

2013/01/29 № 1

-- incrementally narrow down the result set via inner joins
--  the CBO may elect to perform one full index scan combined
--  with cascading index lookups, or as hash aggregates terminated
--  by one nested index lookup into lives - on my machine
--  the latter query plan was selected given my memory settings and
--  histogram
SELECT
  l1.*
 FROM
  lives AS l1
 INNER JOIN (
    SELECT
      usr_id,
      MAX(time_stamp) AS time_stamp_max
     FROM
      lives
     GROUP BY
      usr_id
  ) AS l2
 ON
  l1.usr_id     = l2.usr_id AND
  l1.time_stamp = l2.time_stamp_max
 INNER JOIN (
    SELECT
      usr_id,
      time_stamp,
      MAX(trans_id) AS trans_max
     FROM
      lives
     GROUP BY
      usr_id, time_stamp
  ) AS l3
 ON
  l1.usr_id     = l3.usr_id AND
  l1.time_stamp = l3.time_stamp AND
  l1.trans_id   = l3.trans_max

Наконец, с версии 8.4, поддержки Пост-ГРЭС Функция Окна значение можно записать что-то столь же простое и эффективный как:

Запрос № 3

-- use Window Functions
-- performs a SINGLE scan of the table
SELECT DISTINCT ON (usr_id)
  last_value(time_stamp) OVER wnd,
  last_value(lives_remaining) OVER wnd,
  usr_id,
  last_value(trans_id) OVER wnd
 FROM lives
 WINDOW wnd AS (
   PARTITION BY usr_id ORDER BY time_stamp, trans_id
   ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
 );
82
ответ дан dirkbaechle 24 November 2019 в 07:48
поделиться

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

SELECT l1.*
FROM lives l1 LEFT OUTER JOIN lives l2
  ON (l1.usr_id = l2.usr_id AND (l1.time_stamp < l2.time_stamp 
   OR (l1.time_stamp = l2.time_stamp AND l1.trans_id < l2.trans_id)))
WHERE l2.usr_id IS NULL
ORDER BY l1.usr_id;

я предполагаю, что trans_id уникально, по крайней мере, по любому данному значению time_stamp.

7
ответ дан Bill Karwin 24 November 2019 в 07:48
поделиться
SELECT  l.*
FROM    (
        SELECT DISTINCT usr_id
        FROM   lives
        ) lo, lives l
WHERE   l.ctid = (
        SELECT ctid
        FROM   lives li
        WHERE  li.usr_id = lo.usr_id
        ORDER BY
          time_stamp DESC, trans_id DESC
        LIMIT 1
        )

Создание индекса на (usr_id, time_stamp, trans_id) значительно улучшит этот запрос.

у Вас должен всегда, всегда быть некоторый PRIMARY KEY в Ваших таблицах.

1
ответ дан Quassnoi 24 November 2019 в 07:48
поделиться

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

timestamp   lives_remaining   user_id   trans_id
10:00       4                 3         5
10:00       5                 3         6
10:00       3                 3         1
10:00       2                 3         2

Вы не можете определить от этих данных, которые являются новой записью. Действительно ли это - второе или последнее? Нет никакого вида или макс. () функция, можно обратиться к любым из этих данных, чтобы дать Вам корректный ответ.

Увеличение разрешения метки времени было бы огромной справкой. Так как механизм базы данных сериализирует запросы с достаточным разрешением, можно гарантировать, что никакие две метки времени не будут тем же.

, С другой стороны, используют trans_id, который не перевернется для очень, очень долгое время. Наличие trans_id, который переворачивает средства, которые Вы не можете сказать (для той же метки времени), более свеж ли trans_id 6, чем trans_id 1, если Вы не делаете некоторую сложную математику.

0
ответ дан Barry Brown 24 November 2019 в 07:48
поделиться

Мне нравится стиль ответа Майка Вудхауса на другой странице, которую вы упомянули. Это особенно кратко, когда объект, который максимизируется, представляет собой всего лишь один столбец, и в этом случае подзапрос может просто использовать MAX (some_col) и GROUP BY другие столбцы, но в вашем случае у вас есть количество, состоящее из 2 частей, которое нужно увеличить, вы все равно можете сделать это, используя ORDER BY плюс LIMIT 1 вместо этого (как это сделал Quassnoi):

SELECT * 
FROM lives outer
WHERE (usr_id, time_stamp, trans_id) IN (
    SELECT usr_id, time_stamp, trans_id
    FROM lives sq
    WHERE sq.usr_id = outer.usr_id
    ORDER BY trans_id, time_stamp
    LIMIT 1
)

Я нахожу использование синтаксис конструктора строк WHERE (a, b, c) IN (subquery) хороший, потому что он сокращает объем необходимой многословности.

4
ответ дан 24 November 2019 в 07:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: