Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

uma consulta Rollup com alguma rede lógica usando Oracle SQL


Eu sei que esta é uma pergunta antiga e não será útil para o pôster original, mas eu queria tentar porque era uma pergunta interessante. Eu não testei o suficiente, então eu esperaria que isso ainda precisasse ser corrigido e ajustado. Mas acredito que a abordagem é legítima. Eu não recomendaria usar uma consulta como essa em um produto porque seria difícil de manter ou entender (e não acredito que isso seja realmente escalável). Seria muito melhor criar algumas estruturas de dados alternativas. Dito isso, foi isso que eu executei no 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 o resultado:
     round | net
    -------+-----
         1 | 780
         2 | 420
    (2 rows)

Para dividi-lo em partes, comecei com este conjunto de dados:
    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)

A consulta na cláusula "WITH" adiciona alguns totais em execução à tabela.
  • "previous_net_shares" indica quantas ações estão disponíveis para venda antes do recorde atual. Isso também me diz quantos compartilhamentos de 'VENDA' preciso pular antes de poder começar a alocar para este 'COMPRAR'.

  • "previous_sells" é uma contagem contínua do número de compartilhamentos "SELL" encontrados, portanto, a diferença entre dois "previous_sells" indica o número de compartilhamentos 'SELL' usados ​​naquele 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)
    

Com esta tabela, podemos fazer um self-join onde cada registro "BUY" está associado a cada registro "SELL" futuro. O resultado ficaria assim:
    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 então vem a parte maluca que tenta calcular o número de ações disponíveis para venda na ordem versus o número sobre a ação ainda não vendida para uma compra. Aqui estão algumas notas para ajudar a seguir isso. As "maiores" calls com "0" estão apenas dizendo que não podemos alocar ações se estivermos no negativo.
   -- 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

Obrigado a David por seu assistência