Jag vet att detta är en gammal fråga och inte kommer att vara till nytta för den ursprungliga affischen, men jag ville ta en käft på det här eftersom det var en intressant fråga. Jag testade det inte tillräckligt, så jag förväntar mig att det fortfarande måste korrigeras och justeras. Men jag tror att tillvägagångssättet är legitimt. Jag skulle inte rekommendera att använda en fråga som denna i en produkt eftersom det skulle vara svårt att underhålla eller förstå (och jag tror inte att det är riktigt skalbart). Du skulle vara mycket bättre av att skapa några alternativa datastrukturer. Med det sagt, detta är vad jag körde i 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
Och resultatet:
round | net
-------+-----
1 | 780
2 | 420
(2 rows)
För att dela upp det i bitar började jag med den här datamängden:
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)
Frågan i "WITH"-satsen lägger till några löpande summor till tabellen.
- "previous_net_shares" anger hur många aktier som är tillgängliga att sälja före det aktuella rekordet. Detta talar också om för mig hur många "SÄLJ"-andelar jag måste hoppa över innan jag kan börja allokera det till detta "KÖP".
-
"previous_sells" är en löpande räkning av antalet "SELL"-andelar som påträffats, så skillnaden mellan två "previous_sells" indikerar antalet "SELL"-andelar som användes under den tiden.
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)
Med denna tabell kan vi göra en självanslutning där varje "KÖP"-post associeras med varje framtida "SÄLJ"-post. Resultatet skulle se ut så här:
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)
Och sedan kommer den galna delen som försöker beräkna antalet aktier som är tillgängliga att sälja i ordningen kontra antalet över aktien som ännu inte sålts för ett köp. Här är några anteckningar som hjälper dig att följa det. De "största" samtalen med "0" säger bara att vi inte kan tilldela några aktier om vi är negativa.
-- 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
Tack till David för hans hjälp