Je sais que c'est une vieille question et qu'elle ne sera pas utile à l'affiche originale, mais je voulais tenter ma chance parce que c'était une question intéressante. Je ne l'ai pas suffisamment testé, donc je m'attends à ce que cela doive encore être corrigé et réglé. Mais je crois que l'approche est légitime. Je ne recommanderais pas d'utiliser une requête comme celle-ci dans un produit car elle serait difficile à maintenir ou à comprendre (et je ne pense pas que ce soit vraiment évolutif). Vous feriez bien mieux de créer des structures de données alternatives. Cela dit, voici ce que j'ai exécuté dans 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
Et le résultat :
round | net
-------+-----
1 | 780
2 | 420
(2 rows)
Pour le décomposer en morceaux, j'ai commencé avec cet ensemble de données :
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)
La requête dans la clause "WITH" ajoute des totaux cumulés à la table.
- "previous_net_shares" indique le nombre d'actions disponibles à la vente avant l'enregistrement en cours. Cela m'indique également combien d'actions "VENDRE" je dois ignorer avant de pouvoir commencer à les allouer à cet "ACHAT".
-
"previous_sells" est un décompte du nombre de partages "SELL" rencontrés, donc la différence entre deux "previous_sells" indique le nombre de partages "SELL" utilisés pendant ce temps.
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)
Avec cette table, nous pouvons faire une auto-jointure où chaque enregistrement "ACHETER" est associé à chaque futur enregistrement "VENDRE". Le résultat ressemblerait à ceci :
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)
Et puis vient la partie folle qui essaie de calculer le nombre d'actions disponibles à la vente dans l'ordre par rapport au nombre d'actions non encore vendues pour un achat. Voici quelques notes pour aider à suivre cela. Les "plus grands" appels avec "0" disent simplement que nous ne pouvons pas allouer d'actions si nous sommes dans le négatif.
-- 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
Merci à David pour son assistance