Eu sei que esta é uma pergunta antiga e não será útil para o pôster original, mas eu queria tentar porque era uma pergunta interessante. Eu não testei o suficiente, então eu esperaria que isso ainda precisasse ser corrigido e ajustado. Mas acredito que a abordagem é legítima. Eu não recomendaria usar uma consulta como essa em um produto porque seria difícil de manter ou entender (e não acredito que isso seja realmente escalável). Seria muito melhor criar algumas estruturas de dados alternativas. Dito isso, foi isso que eu executei no 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
E o resultado:
round | net
-------+-----
1 | 780
2 | 420
(2 rows)
Para dividi-lo em partes, comecei com este conjunto de dados:
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)
A consulta na cláusula "WITH" adiciona alguns totais em execução à tabela.
- "previous_net_shares" indica quantas ações estão disponíveis para venda antes do recorde atual. Isso também me diz quantos compartilhamentos de 'VENDA' preciso pular antes de poder começar a alocar para este 'COMPRAR'.
-
"previous_sells" é uma contagem contínua do número de compartilhamentos "SELL" encontrados, portanto, a diferença entre dois "previous_sells" indica o número de compartilhamentos 'SELL' usados naquele momento.
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)
Com esta tabela, podemos fazer um self-join onde cada registro "BUY" está associado a cada registro "SELL" futuro. O resultado ficaria assim:
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)
E então vem a parte maluca que tenta calcular o número de ações disponíveis para venda na ordem versus o número sobre a ação ainda não vendida para uma compra. Aqui estão algumas notas para ajudar a seguir isso. As "maiores" calls com "0" estão apenas dizendo que não podemos alocar ações se estivermos no negativo.
-- 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
Obrigado a David por seu assistência