Получите номер заказа, при котором общая сумма заказов составит 1000

У меня есть таблица заказов, и в каждой строке есть столбец под названием цена. В каждом из этих заказов также есть столбец created_at, в котором будет указано, когда этот заказ был создан.

Что было бы хорошим способом узнать, в каком порядке общая сумма цен превысит 1000 долларов?

Итак, представьте, что у меня есть три заказа, которые выглядят так:

Order 1: price: $800 - created_at: 2013/07/11 

Order 2: price: $100 - created_at: 2013/07/13 

Order 3: price: $300 - created_at: 2013/07/14 

Мне было бы интересно узнать, что заказ 3 - это тот, который заставил меня передать более 1000 долларов, потому что если мы добавим 800 + 100 + 300 долларов, это именно те 300 долларов, которые сделали общую сумму больше 1000 долларов.

Какой запрос я мог выполнить, чтобы найти это?


person Hommer Smith    schedule 18.07.2013    source источник
comment
Что отличает заказы? Или будет несколько результатов или только один?   -  person Hart CO    schedule 18.07.2013
comment
Первичный ключ - это столбец с именем uuid, который представляет собой уникальную строку.   -  person Hommer Smith    schedule 18.07.2013


Ответы (2)


После вычисления текущей суммы с помощью оконной агрегатной функции sum() просто выберите первую строку в соответствии с created_at, которая превышает 1000:

SELECT *
FROM (
   SELECT order_id, created_at
        , sum(price) OVER (ORDER BY created_at) AS sum_price
   FROM   orders
   ) sub
WHERE  sum_price >= 1000
ORDER  BY created_at 
LIMIT  1;

Это должно быть быстрее, чем версия @ Gordon, потому что выбор первого в том же порядке, который уже используется в оконной функции, намного дешевле, чем вычисление значения для каждой строки, которая не может быть саргируемой.

Я использую sum_price >= 1000, так что достижение 1000 тоже подходит. Если допускается только превышение, используйте > вместо >=.

В руководстве по оконным функциям говорится:

В дополнение к этим функциям любая встроенная или определяемая пользователем агрегатная функция может использоваться как оконная функция.

Следует отметить, что этот запрос всегда возвращает ровно одну строку, в отличие от запроса @Gordon. В случае, когда несколько строк с одинаковыми created_at пересекают барьер 1000, все они будут квалифицированы в ответе Гордона (или он потерпит неудачу, см. Ниже), в то время как в моем случае выбрано только one. Это будет произвольно, пока вы не добавляете больше элементов в ORDER BY в качестве разрешения конфликтов. Нравиться:

ORDER BY created_at, order_id

В этом запросе есть два экземпляра ORDER BY, и так уж получилось, что вы можете изменить один или оба, чтобы заставить его работать. Сделайте это для обоих, чтобы порядок сортировки был одинаковым, это должно быть быстрее всего.

На самом деле, версия Гордона не прошла бы полностью в этом тестовом примере:

CREATE TEMP TABLE orders(order_id int, price int, created_at date);

INSERT INTO orders VALUES
  (1, 500, '2013-07-01')
 ,(2, 400, '2013-07-02')
 ,(3, 100, '2013-07-03')
 ,(4, 100, '2013-07-03')
 ,(5, 100, '2013-07-03');

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

Или вы можете изменить определение кадра для оконной функции на:

ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

Прочтите руководство, написанное мелким шрифтом.

Но в любом случае медленнее.

-> SQLfiddle

person Erwin Brandstetter    schedule 18.07.2013

Для этого вам нужна совокупная сумма, которую Postgres предоставляет как оконную функцию:

select o.*
from (select o.*,
             sum(o2.price) over (order by created_at) as cumsum
      from orders o
     ) o
where 1000 > cumsum - price and 1000 <= cumsum;

Предложение where просто штрафует строку, в которой добавленная цена сначала превышает 1000 долларов.

person Gordon Linoff    schedule 18.07.2013
comment
Привет, Гордон, что бы было за o2? Полагаю, это изнутри из приказов о2. - person Hommer Smith; 18.07.2013
comment
@HommerSmith: o2 это просто опечатка. Здесь вы можете просто удалить все квалификации таблиц и псевдонимы таблиц. Однако этот запрос может полностью завершиться ошибкой для дубликата created_at. Я добавил абзац к своему ответу. - person Erwin Brandstetter; 18.07.2013