Jeg ved godt, at dette er et gammelt spørgsmål og ikke vil være til nytte for den originale plakat, men jeg ville gerne prøve det, fordi det var et interessant spørgsmål. Jeg testede det ikke nok, så jeg forventer, at det stadig skal rettes og justeres. Men jeg mener, at tilgangen er legitim. Jeg vil ikke anbefale at bruge en forespørgsel som denne i et produkt, fordi det ville være svært at vedligeholde eller forstå (og jeg tror ikke på, at dette virkelig er skalerbart). Du ville være meget bedre stillet at oprette nogle alternative datastrukturer. Når det er sagt, er det dette, jeg kørte 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
Og resultatet:
round | net
-------+-----
1 | 780
2 | 420
(2 rows)
For at bryde det ned i stykker startede jeg med dette datasæt:
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)
Forespørgslen i "WITH"-klausulen tilføjer nogle løbende totaler til tabellen.
- "previous_net_shares" angiver, hvor mange aktier der er tilgængelige for salg før den aktuelle rekord. Dette fortæller mig også, hvor mange 'SÆLG'-andele jeg skal springe over, før jeg kan begynde at allokere det til dette 'KØB'.
-
"previous_sells" er en løbende optælling af antallet af "SELL"-andele, der er stødt på, så forskellen mellem to "previous_sells" angiver antallet af "SELL"-andele, der blev brugt i det pågældende tidsrum.
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 denne tabel kan vi lave en selv-join, hvor hver "KØB"-record er knyttet til hver fremtidige "SÆLG"-post. Resultatet ville se sådan ud:
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)
Og så kommer den skøre del, der forsøger at beregne antallet af tilgængelige aktier til salg i rækkefølgen i forhold til antallet af aktier, der endnu ikke er solgt til et køb. Her er nogle bemærkninger til at hjælpe med at følge det. De "største" opkald med "0" siger bare, at vi ikke kan tildele nogen aktier, hvis vi er negative.
-- 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
Tak til David for hans assistance