Saya tahu ini adalah pertanyaan lama dan tidak akan berguna untuk poster aslinya, tetapi saya ingin mencoba ini karena ini adalah pertanyaan yang menarik. Saya tidak cukup mengujinya, jadi saya berharap ini masih perlu diperbaiki dan disetel. Tapi saya percaya pendekatan itu sah. Saya tidak akan merekomendasikan menggunakan kueri seperti ini dalam suatu produk karena akan sulit untuk dipertahankan atau dipahami (dan saya tidak percaya ini benar-benar terukur). Anda akan jauh lebih baik membuat beberapa struktur data alternatif. Karena itu, inilah yang saya jalankan di 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
Dan hasilnya:
round | net
-------+-----
1 | 780
2 | 420
(2 rows)
Untuk memecahnya menjadi beberapa bagian, saya mulai dengan kumpulan data ini:
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)
Kueri dalam klausa "DENGAN" menambahkan beberapa total yang berjalan ke tabel.
- "previous_net_shares" menunjukkan berapa banyak saham yang tersedia untuk dijual sebelum rekor saat ini. Ini juga memberi tahu saya berapa banyak saham 'JUAL' yang harus saya lewati sebelum saya dapat mulai mengalokasikannya ke 'BELI' ini.
-
"previous_sells" adalah hitungan berjalan dari jumlah saham "JUAL" yang ditemukan, jadi perbedaan antara dua "previous_sells" menunjukkan jumlah saham 'JUAL' yang digunakan pada waktu itu.
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)
Dengan tabel ini, kita dapat melakukan self-join di mana setiap record "BUY" dikaitkan dengan setiap record "SELL" yang akan datang. Hasilnya akan terlihat seperti ini:
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)
Dan kemudian muncul bagian gila yang mencoba menghitung jumlah saham yang tersedia untuk dijual dalam pesanan vs jumlah saham yang belum terjual untuk dibeli. Berikut adalah beberapa catatan untuk membantu mengikutinya. Panggilan "terbesar" dengan "0" hanya mengatakan bahwa kami tidak dapat mengalokasikan bagian apa pun jika kami berada di posisi negatif.
-- 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
Terima kasih kepada David untuk bantuan