Oracle
 sql >> Database >  >> RDS >> Oracle

una query di rollup con una rete logica utilizzando Oracle SQL

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