Знам, че това е стар въпрос и няма да е от полза за оригиналния плакат, но исках да се сблъскам с него, защото беше интересен въпрос. Не го тествах достатъчно, така че бих очаквал, че това все още трябва да бъде коригирано и настроено. Но смятам, че подходът е легитимен. Не бих препоръчал използването на заявка като тази в продукт, защото би било трудно да се поддържа или разбира (и не вярвам, че това е наистина мащабируемо). Ще бъде много по-добре да създадете някои алтернативни структури от данни. Като казах това, това е, което пуснах в 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", така че разликата между две "previous_sells" показва броя на акциите "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
Благодаря на Дейвид за неговия помощ