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

en Rollup-forespørgsel med noget logisk netting ved hjælp af Oracle SQL

Jeg ved godt, at dette er et gammelt spørgsmål og ikke vil være til nytte for den originale plakat, men jeg ville gerne prøve det, fordi det var et interessant spørgsmål. Jeg testede det ikke nok, så jeg forventer, at det stadig skal rettes og justeres. Men jeg mener, at tilgangen er legitim. Jeg vil ikke anbefale at bruge en forespørgsel som denne i et produkt, fordi det ville være svært at vedligeholde eller forstå (og jeg tror ikke på, at dette virkelig er skalerbart). Du ville være meget bedre stillet at oprette nogle alternative datastrukturer. Når det er sagt, er det dette, jeg kørte i 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

Og resultatet:

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

For at bryde det ned i stykker startede jeg med dette datasæt:

    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)
 

Forespørgslen i "WITH"-klausulen tilføjer nogle løbende totaler til tabellen.

  • "previous_net_shares" angiver, hvor mange aktier der er tilgængelige for salg før den aktuelle rekord. Dette fortæller mig også, hvor mange 'SÆLG'-andele jeg skal springe over, før jeg kan begynde at allokere det til dette 'KØB'.
  • "previous_sells" er en løbende optælling af antallet af "SELL"-andele, der er stødt på, så forskellen mellem to "previous_sells" angiver antallet af "SELL"-andele, der blev brugt i det pågældende tidsrum.

     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)
     

Med denne tabel kan vi lave en selv-join, hvor hver "KØB"-record er knyttet til hver fremtidige "SÆLG"-post. Resultatet ville se sådan ud:

    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)
 

Og så kommer den skøre del, der forsøger at beregne antallet af tilgængelige aktier til salg i rækkefølgen i forhold til antallet af aktier, der endnu ikke er solgt til et køb. Her er nogle bemærkninger til at hjælpe med at følge det. De "største" opkald med "0" siger bare, at vi ikke kan tildele nogen aktier, hvis vi er negative.

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

Tak til David for hans assistance




  1. Send kommandolinje args til sql (Postgres)

  2. SQL til at bestemme minimum sekventielle dages adgang?

  3. MySQL:summen af ​​hver dag

  4. Sådan laver du en visningskolonne IKKE NULL