Oracle
 sql >> Base de données >  >> RDS >> Oracle

une requête Rollup avec une compensation logique à l'aide d'Oracle SQL

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