簡體   English   中英

如何將這個復雜的SQL轉換為Django模型查詢?

[英]How do I convert this complex SQL into a Django model query?

我正在編寫一個Python / Django應用程序來進行一些庫存分析。

我有兩個非常簡單的模型,如下所示:

class Stock(models.Model):
    symbol = models.CharField(db_index=True, max_length=5, null=False, editable=False, unique=True)

class StockHistory(models.Model):
    stock = models.ForeignKey(Stock, related_name='StockHistory_stock', editable=False)
    trading_date = models.DateField(db_index=True, null=False, editable=False)
    close = models.DecimalField(max_digits=12, db_index=True, decimal_places=5, null=False, editable=False)

    class Meta:
        unique_together = ('stock', 'trading_date')

這是我填充的虛擬數據:

import datetime
a = Stock.objects.create(symbol='A')
b = Stock.objects.create(symbol='B')
c = Stock.objects.create(symbol='C')
d = Stock.objects.create(symbol='D')

StockHistory.objects.create(trading_date=datetime.date(2018,1,1), close=200, stock=a)
StockHistory.objects.create(trading_date=datetime.date(2018,1,2), close=150, stock=a)
StockHistory.objects.create(trading_date=datetime.date(2018,1,3), close=120, stock=a)
StockHistory.objects.create(trading_date=datetime.date(2018,4,28), close=105, stock=a)
StockHistory.objects.create(trading_date=datetime.date(2018,5,3), close=105, stock=a)

StockHistory.objects.create(trading_date=datetime.date(2017,5,2), close=400, stock=b)
StockHistory.objects.create(trading_date=datetime.date(2017,11,11), close=200, stock=b)
StockHistory.objects.create(trading_date=datetime.date(2017,11,12), close=300, stock=b)
StockHistory.objects.create(trading_date=datetime.date(2017,11,13), close=400, stock=b)
StockHistory.objects.create(trading_date=datetime.date(2017,11,14), close=500, stock=b)

StockHistory.objects.create(trading_date=datetime.date(2018,4,28), close=105, stock=c)
StockHistory.objects.create(trading_date=datetime.date(2018,4,29), close=106, stock=c)
StockHistory.objects.create(trading_date=datetime.date(2018,4,30), close=107, stock=c)
StockHistory.objects.create(trading_date=datetime.date(2018,5,1), close=108, stock=c)
StockHistory.objects.create(trading_date=datetime.date(2018,5,2), close=109, stock=c)
StockHistory.objects.create(trading_date=datetime.date(2018,5,3), close=110, stock=c)
StockHistory.objects.create(trading_date=datetime.date(2018,5,4), close=90, stock=c)

我想找到過去一周內年度低點的所有股票。

但是為了使這個問題更簡單,只要假設我想找到自'2017-05-04'發生在'2018-04-30'之后或之后的最低點的所有股票。 下面是我寫的SQL來找到它。 有用。

但是我需要幫助找出要寫的Django Query以獲得與此SQL相同的結果。 我該怎么做?

mysql> select
    ->     s.symbol,
    ->     sh.trading_date,
    ->     low_table.low
    -> from
    ->     (
    ->         select
    ->             stock_id,
    ->             min(close) as low
    ->         from
    ->             stocks_stockhistory
    ->         where
    ->             trading_date >= '2017-05-04'
    ->         group by
    ->             stock_id
    ->     ) as low_table,
    ->     stocks_stockhistory as sh,
    ->     stocks_stock as s
    -> where
    ->     sh.stock_id = low_table.stock_id
    ->     and sh.stock_id = s.id
    ->     and sh.close = low_table.low
    ->     and sh.trading_date >= '2018-04-30'
    -> order by
    ->     s.symbol asc;
+--------+--------------+-----------+
| symbol | trading_date | low       |
+--------+--------------+-----------+
| A      | 2018-05-03   | 105.00000 |
| C      | 2018-05-04   |  90.00000 |
+--------+--------------+-----------+
2 rows in set (0.02 sec)

編輯:我設法使用Django子查詢改革解決方案。

我們可以使用Django aggregates with SubQuery expressionsaggregates with SubQuery expressions將查詢轉換為Django ORM:

  1. 創建子查詢以檢索每個symbol的最低close

     from django.db.models import OuterRef, Subquery, Min lows = StockHistory.objects.filter( stock=OuterRef('stock'), trading_date__gte='2017-05-04' ).values('stock__symbol') .annotate(low=Min('close')) .filter(trading_date__gte='2018-04-30') 
    • 分解:

      • filter查詢集以僅獲得具有trading_date >= '2017-05-04'的股票。
      • “GROUP BY” stock__symbol (在Djnago中分組的示例: GROUP BY ... MIN/MAXGROUP BY ... COUNT/SUM )。
      • 為每個元素annotate最低( low )價格。
      • 再次filter查詢集以僅獲取在trading_date >= '2018-04-30'上發生low字段的對象。
    • 中級結果:

      雖然我們無法在此階段獲得結果,但子查詢將如下所示:

       [ {'stock__symbol': 'A', 'low': Decimal('105.00000')}, {'stock__symbol': 'C', 'low': Decimal('90.00000')} ] 

      我們錯過了trading_date

  2. 利用子查詢來檢索特定的StockHistory對象:

     StockHistory.objects.filter( stock__symbol=Subquery(lows.values('stock__symbol')), close=Subquery(lows.values('low')), trading_date__gte='2018-04-30' ).values('stock__symbol', 'trading_date', 'close') .order_by('stock__symbol') 
    • 分解:

      • lows.values('stock__symbol')和lows.values('low')從子查詢中檢索相應的值。
      • 根據lows子查詢值filter查詢集。 同時filter指定日期,以消除在該日期之前發生的低close價。
      • 獲取指定的values
      • 通過stock__symbol訂購結果(默認為ascending )。
    • 結果:

       [ { 'close': Decimal('105.00000'), 'trading_date': datetime.date(2018, 5, 3), 'stock__symbol': 'A' }, { 'close': Decimal('90.00000'), 'trading_date': datetime.date(2018, 5, 4), 'stock__symbol': 'C' } ] 

對於較新版本的Django(1.11,2.0):

from django.db.models import Min
low_stocks_qs = StockHistory.objects.filter(trading_date__gt='2017-05-04').annotate(low=Min('close')).filter(trading_date__gte='2018-04-30').order_by('stock__symbol')

您可以遍歷查詢集以獲取low和stock.symbol的單個值,可能是這樣的:

low_stocks_dict = {}
for inst in low_stocks_qs:
    low_stocks_dict[inst.stock.Symbol] = inst.low

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM