Объект 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
На таблице с 158k псевдослучайными строками (usr_id равномерно распределенный между 0 и 10k, trans_id
равномерно распределенный между 0 и 30),
стоимостью запроса, ниже, я обращаюсь к оценке затрат основанного на стоимости оптимизатора Пост-ГРЭС (со значением по умолчанию Пост-ГРЭС xxx_cost
значения), который является взвешенной функциональной оценкой необходимого ввода-вывода и ресурсов ЦП; можно получить, это путем разжигания PgAdminIII и выполнения "Запрашивает/Объясняет (F7)" на запросе с, "Запрашивают/Объясняют опции" набор, чтобы "Проанализировать"
usr_id
, trans_id
, time_stamp
)) usr_id
, trans_id
)) usr_id
, trans_id
, time_stamp
)) usr_id
, EXTRACT(EPOCH FROM time_stamp)
, trans_id
)) 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 2013/01/29 № 1 -- 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]
-- 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
);
Вот другой метод, который, оказывается, не использует связанных подзапросов или 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
.
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
в Ваших таблицах.
Я думаю, что у Вас есть одна основная проблема здесь: нет никакого монотонно увеличивающегося "счетчика", чтобы гарантировать, что данная строка произошла позже вовремя, чем другой. Возьмите этот пример:
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, если Вы не делаете некоторую сложную математику.
Мне нравится стиль ответа Майка Вудхауса на другой странице, которую вы упомянули. Это особенно кратко, когда объект, который максимизируется, представляет собой всего лишь один столбец, и в этом случае подзапрос может просто использовать 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)
хороший, потому что он сокращает объем необходимой многословности.