Oracle
 sql >> Datenbank >  >> RDS >> Oracle

eine Rollup-Abfrage mit logischem Netting unter Verwendung von Oracle SQL

Ich weiß, dass dies eine alte Frage ist und für das ursprüngliche Poster nicht von Nutzen sein wird, aber ich wollte es versuchen, weil es eine interessante Frage war. Ich habe es nicht genug getestet, also würde ich erwarten, dass dies noch korrigiert und abgestimmt werden muss. Aber ich halte den Ansatz für legitim. Ich würde die Verwendung einer solchen Abfrage in einem Produkt nicht empfehlen, da sie schwierig zu warten oder zu verstehen wäre (und ich glaube nicht, dass dies wirklich skalierbar ist). Sie wären viel besser dran, einige alternative Datenstrukturen zu erstellen. Abgesehen davon habe ich Folgendes in Postgresql 9.1 ausgeführt:

    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

Und das Ergebnis:

     round | net
    -------+-----
         1 | 780
         2 | 420
    (2 rows)

Um es in Stücke zu zerlegen, habe ich mit diesem Datensatz begonnen:

    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)

Die Abfrage in der "WITH"-Klausel fügt der Tabelle einige laufende Summen hinzu.

  • " previous_net_shares " gibt an, wie viele Aktien vor dem aktuellen Rekord verkauft werden können. Dies sagt mir auch, wie viele 'VERKAUFEN'-Anteile ich überspringen muss, bevor ich anfangen kann, sie diesem 'KAUFEN' zuzuweisen.
  • "vorherige_Verkäufe" ist eine laufende Zählung der Anzahl der gefundenen "SELL"-Aktien, sodass die Differenz zwischen zwei "vorherigen_Verkäufen" die Anzahl der in dieser Zeit verwendeten "SELL"-Aktien angibt.

     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)
    

Mit dieser Tabelle können wir einen Self-Join durchführen, bei dem jeder "BUY"-Datensatz mit jedem zukünftigen "SELL"-Datensatz verknüpft ist. Das Ergebnis würde so aussehen:

    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)

Und dann kommt der verrückte Teil, der versucht, die Anzahl der zum Verkauf stehenden Aktien in der Reihenfolge gegen die Anzahl der noch nicht verkauften Aktien für einen Kauf zu berechnen. Hier sind einige Hinweise, die dabei helfen, dem zu folgen. Die "größten" Calls mit "0" sagen nur aus, dass wir keine Aktien zuteilen können, wenn wir im Minus sind.

   -- 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

Danke an David für seinen Hilfe