Я знаю, что это старый вопрос, и он не будет полезен для оригинального плаката, но я хотел бы попробовать это, потому что это был интересный вопрос. Я не проверил это достаточно, поэтому я ожидал, что это все еще нужно исправить и настроить. Но я считаю, что подход является законным. Я бы не рекомендовал использовать такой запрос в продукте, потому что его было бы сложно поддерживать или понимать (и я не верю, что это действительно масштабируемо). Вам было бы гораздо лучше создать несколько альтернативных структур данных. Сказав это, я запустил Postgresql 9.1:
WITH x AS (
SELECT round, action
,ABS(shares) AS shares
,profitpershare
,COALESCE( SUM(shares) OVER(ORDER BY round, action
ROWS BETWEEN UNBOUNDED PRECEDING
AND 1 PRECEDING)
, 0) AS previous_net_shares
,COALESCE( ABS( SUM(CASE WHEN action = 'SELL' THEN shares ELSE 0 END)
OVER(ORDER BY round, action
ROWS BETWEEN UNBOUNDED PRECEDING
AND 1 PRECEDING) ), 0 ) AS previous_sells
FROM AuctionResults
ORDER BY 1,2
)
SELECT round, shares * profitpershare - deduction AS net
FROM (
SELECT buy.round, buy.shares, buy.profitpershare
,SUM( LEAST( LEAST( sell.shares, GREATEST(buy.shares - (sell.previous_sells - buy.previous_sells), 0)
,GREATEST(sell.shares + (sell.previous_sells - buy.previous_sells) - buy.previous_net_shares, 0)
)
) * sell.profitpershare ) AS deduction
FROM x buy
,x sell
WHERE sell.round > buy.round
AND buy.action = 'BUY'
AND sell.action = 'SELL'
GROUP BY buy.round, buy.shares, buy.profitpershare
) AS y
И результат:
round | net
-------+-----
1 | 780
2 | 420
(2 rows)
Чтобы разбить его на части, я начал с этого набора данных:
CREATE TABLE AuctionResults( round int, action varchar(4), shares int, profitpershare int);
INSERT INTO AuctionResults VALUES(1, 'BUY', 6, 200);
INSERT INTO AuctionResults VALUES(2, 'BUY', 5, 100);
INSERT INTO AuctionResults VALUES(2, 'SELL',-2, 50);
INSERT INTO AuctionResults VALUES(3, 'SELL',-5, 80);
INSERT INTO AuctionResults VALUES(4, 'SELL', -4, 150);
select * from auctionresults;
round | action | shares | profitpershare
-------+--------+--------+----------------
1 | BUY | 6 | 200
2 | BUY | 5 | 100
2 | SELL | -2 | 50
3 | SELL | -5 | 80
4 | SELL | -4 | 150
(5 rows)
Запрос в предложении WITH добавляет некоторые промежуточные итоги в таблицу.
- "previous_net_shares" указывает, сколько акций доступно для продажи до текущей записи. Это также говорит мне, сколько акций «ПРОДАВАТЬ» мне нужно пропустить, прежде чем я смогу начать распределять их по этому «ПОКУПАТЬ».
«previous_sells» - это текущий счет количества встреченных акций «SELL», поэтому разница между двумя предыдущими «продажами» указывает количество акций «SELL», использованных за это время.
round | action | shares | profitpershare | previous_net_shares | previous_sells
-------+--------+--------+----------------+---------------------+----------------
1 | BUY | 6 | 200 | 0 | 0
2 | BUY | 5 | 100 | 6 | 0
2 | SELL | 2 | 50 | 11 | 0
3 | SELL | 5 | 80 | 9 | 2
4 | SELL | 4 | 150 | 4 | 7
(5 rows)
С помощью этой таблицы мы можем выполнить самостоятельное объединение, где каждая запись «ПОКУПАТЬ» связана с каждой будущей записью «ПРОДАЖА». Результат будет выглядеть так:
SELECT buy.round, buy.shares, buy.profitpershare
,sell.round AS sellRound, sell.shares AS sellShares, sell.profitpershare AS sellProfitpershare
FROM x buy
,x sell
WHERE sell.round > buy.round
AND buy.action = 'BUY'
AND sell.action = 'SELL'
round | shares | profitpershare | sellround | sellshares | sellprofitpershare
-------+--------+----------------+-----------+------------+--------------------
1 | 6 | 200 | 2 | 2 | 50
1 | 6 | 200 | 3 | 5 | 80
1 | 6 | 200 | 4 | 4 | 150
2 | 5 | 100 | 3 | 5 | 80
2 | 5 | 100 | 4 | 4 | 150
(5 rows)
И затем приходит сумасшедшая часть, которая пытается вычислить количество акций, доступных для продажи в заказе, по сравнению с количеством акций, которые еще не проданы для покупки. Вот несколько заметок, которые помогут вам в этом. «Величайшие» коллы с «0» просто говорят, что мы не можем распределить какие-либо акции, если мы находимся в минусе.
-- allocated sells
sell.previous_sells - buy.previous_sells
-- shares yet to sell for this buy, if < 0 then 0
GREATEST(buy.shares - (sell.previous_sells - buy.previous_sells), 0)
-- number of sell shares that need to be skipped
buy.previous_net_shares
Спасибо Дэвиду за его помощь