Oracle
 sql >> Baza danych >  >> RDS >> Oracle

Zapytanie zbiorcze z pewnym logicznym połączeniem przy użyciu Oracle SQL

Wiem, że to stare pytanie i nie przyda się do oryginalnego plakatu, ale chciałem się tym zająć, ponieważ było to interesujące pytanie. Nie przetestowałem tego wystarczająco, więc spodziewałbym się, że nadal trzeba to poprawić i dostroić. Ale uważam, że takie podejście jest uzasadnione. Nie polecałbym używania takiego zapytania w produkcie, ponieważ byłoby to trudne do utrzymania lub zrozumienia (i nie wierzę, że jest to naprawdę skalowalne). Lepiej byłoby stworzyć alternatywne struktury danych. Powiedziawszy to, oto co uruchomiłem w 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 wynik:

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

Aby podzielić to na kawałki, zacząłem od tego zestawu danych:

    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)

Zapytanie w klauzuli "WITH" dodaje do tabeli pewne bieżące sumy.

  • "previous_net_shares" wskazuje, ile akcji jest dostępnych do sprzedaży przed bieżącym rekordem. To również mówi mi, ile akcji „SPRZEDAJ” muszę pominąć, zanim będę mógł je przydzielić do tego „KUP”.
  • „previous_sells” to bieżąca liczba napotkanych akcji „SPRZEDAJ”, więc różnica między dwoma „previous_sells” wskazuje liczbę akcji „SPRZEDAJ” użytych w tym czasie.

     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)
    

Za pomocą tej tabeli możemy wykonać samosprzężenie, w którym każdy rekord „KUP” jest powiązany z każdym przyszłym rekordem „Sprzedaj”. Wynik wyglądałby tak:

    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 potem przychodzi szalona część, która próbuje obliczyć liczbę akcji dostępnych do sprzedania w zamówieniu w porównaniu z liczbą akcji, które nie zostały jeszcze sprzedane w celu zakupu. Oto kilka uwag, które pomogą ci to śledzić. „Największe” wywołania z „0” mówią po prostu, że nie możemy przydzielić żadnych udziałów, jeśli mamy negatywny wynik.

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

Dziękuję Davidowi za jego pomoc




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. pobieranie długich surowych danych do zmiennej clob

  2. Jak odświeżyć zmaterializowany widok w Oracle?

  3. Entity Framework połącz się z Oracle:ODP dla .NET nie obsługuje czasu

  4. SQL — po co dopasowywać spacje podczas porównywania znaków?

  5. Ograniczenie Oracle Client Blob 10K z NHibernate i Mono?