sql >> Databáze >  >> RDS >> Oracle

souhrnný dotaz s logickým síťováním pomocí Oracle SQL

Vím, že je to stará otázka a nebude k užitku původnímu plakátu, ale chtěl jsem se do toho pustit, protože to byla zajímavá otázka. Nedostatečně jsem to otestoval, takže bych očekával, že to bude ještě potřeba opravit a doladit. Ale věřím, že tento přístup je legitimní. Nedoporučoval bych používat takovýto dotaz v produktu, protože by bylo obtížné ho udržovat nebo pochopit (a nevěřím, že je to skutečně škálovatelné). Bylo by mnohem lepší vytvořit nějaké alternativní datové struktury. Jak jsem řekl, toto jsem spustil v 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

A výsledek:

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

Abych to rozdělil na kousky, začal jsem s tímto souborem dat:

    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)

Dotaz v klauzuli "WITH" přidá do tabulky nějaké průběžné součty.

  • „previous_net_shares“ udává, kolik akcií je k dispozici k prodeji před aktuálním rekordem. To mi také říká, kolik akcií „PRODEJ“ musím přeskočit, než je budu moci začít přidělovat tomuto „KOUPIT“.
  • "previous_sells" je průběžný počet zaznamenaných "SELL" podílů, takže rozdíl mezi dvěma "previous_sells" udává počet "SELL" podílů použitých v dané době.

     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)
    

Pomocí této tabulky můžeme provést vlastní spojení, kde je každý záznam „KOUPIT“ spojen s každým budoucím záznamem „PRODEJ“. Výsledek by vypadal takto:

    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)

A pak přichází ta bláznivá část, která se pokouší spočítat počet akcií, které jsou k dispozici k prodeji v objednávce, oproti počtu akcií, které ještě nebyly prodány na nákup. Zde je několik poznámek, které vám to pomohou. "Největší" hovory s "0" jen říkají, že nemůžeme přidělit žádné podíly, pokud jsme v záporu.

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

Děkujeme Davidovi za jeho pomoc




  1. MySQL efektivně ukládá neorientované okraje grafu

  2. Chyba při instalaci serveru Mysql2 gem- 10.6 Server

  3. Jak přidám indexy do tabulek MySQL?

  4. Jak najít vztah z databáze Snomed Postgres Sql