So che questa è una vecchia domanda e non sarà utile per il poster originale, ma volevo provarci perché era una domanda interessante. Non l'ho testato abbastanza, quindi mi aspetto che questo debba ancora essere corretto e messo a punto. Ma credo che l'approccio sia legittimo. Non consiglierei di utilizzare una query come questa in un prodotto perché sarebbe difficile da mantenere o comprendere (e non credo che sia davvero scalabile). Sarebbe molto meglio creare alcune strutture di dati alternative. Detto questo, questo è ciò che ho eseguito in 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 il risultato:
round | net
-------+-----
1 | 780
2 | 420
(2 rows)
Per scomporlo in pezzi, ho iniziato con questo set di dati:
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 query nella clausola "WITH" aggiunge alcuni totali parziali alla tabella.
- "previous_net_shares" indica quante azioni sono disponibili per la vendita prima del record corrente. Questo mi dice anche quante condivisioni "VENDI" devo saltare prima di poter iniziare ad assegnarle a questo "ACQUISTA".
-
"previous_sells" è un conteggio progressivo del numero di condivisioni "SELL" incontrate, quindi la differenza tra due "previous_sells" indica il numero di condivisioni "VENDI" utilizzate in quel 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)
Con questa tabella, possiamo eseguire un self-join in cui ogni record "ACQUISTA" è associato a ogni record "VENDITA" futuro. Il risultato sarebbe simile a questo:
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 poi arriva la parte folle che cerca di calcolare il numero di azioni disponibili per la vendita nell'ordine rispetto al numero di azioni non ancora vendute per un acquisto. Ecco alcune note per aiutare a seguirlo. Le chiamate "più grandi" con "0" stanno solo dicendo che non possiamo allocare azioni se siamo negativi.
-- 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
Grazie a David per il suo assistenza