ORDER BY SQLAlchemy 中的子查询

Posted

技术标签:

【中文标题】ORDER BY SQLAlchemy 中的子查询【英文标题】:Subquery in ORDER BY SQLAlchemy 【发布时间】:2016-01-21 14:27:26 【问题描述】:

我正在尝试将 SQL 查询转换为 SQLAlchemy 查询语言。 为了稍微理解查询的意义,我有一个表security和一个表valuation证券表描述了我在市场上关注的不同证券(股票):

 id   |           bbg_ticker
------+-------------------------
    1 | 3993 HK Equity
    2 | A2A IM Equity
    3 | AA UN Equity
    4 | AA/ LN Equity
    5 | AAL LN Equity
    6 | AALB NA Equity
    7 | ABBN VX Equity

估值表描述了一种证券一天的价值:

security_id |    date    |  px_close   |   volume
------------+------------+-------------+-------------
          1 | 2015-05-18 |       6.754 |     9890000
          1 | 2015-05-19 |       6.802 |    11660773
          1 | 2015-05-20 |       6.802 |    12674694
          1 | 2015-05-21 |       6.735 |     5533000
          1 | 2015-05-22 |        6.85 |    10096288
          2 | 2015-05-18 |      1.0558 |    32198683
          2 | 2015-05-19 |      1.0577 |    17630748
          2 | 2015-05-20 |      1.0606 |    11990913
          2 | 2015-05-21 |      1.0722 |    24492170
          2 | 2015-05-22 |      1.0887 |    28795865
          3 | 2015-05-18 |     13.3587 |     3107029
          3 | 2015-05-19 |     13.0397 |     6276252
          3 | 2015-05-20 |     13.0297 |     3746343
          3 | 2015-05-21 |     12.9599 |     4023997
          3 | 2015-05-22 |     12.9001 |     3438908
          4 | 2015-05-18 |     403.949 |     2059825
          4 | 2015-05-19 |     404.937 |     1153599
          4 | 2015-05-20 |     405.035 |      769304
          4 | 2015-05-21 |     403.455 |      586507
          4 | 2015-05-22 |     399.998 |      878268
          5 | 2015-05-18 |    1049.328 |     4957938

我想做的是在一个精确的日期获得按交易量分类的第 10 佳证券。问题是有时这个特定日期(例如周末)没有数据,所以我想取最后一个音量值(过去最近的)。

我在纯 SQL 中找到了解决方案(这里是 2015 年 5 月 23 日的示例):

SELECT s.bbg_ticker
FROM security s
INNER JOIN valuation v1
    ON v1.security_id = s.id
    AND v1.volume IS NOT NULL
    AND v1.px_close iS NOT NULL
    AND v1.date > '2015-05-16'     # because I don't want too old values..
    AND v1.date <= '2015-05-23'
GROUP BY s.id
ORDER BY (SELECT v.volume
    FROM valuation v
    WHERE v.security_id = s.id
        AND v.volume IS NOT NULL
        AND v.px_close IS NOT NULL
        AND v.date > '2015-05-16'  # same
        AND v.date <= '2015-05-23'
    ORDER BY v.date DESC LIMIT 1
) DESC
LIMIT 10

我想做同样的事情,但使用 SQLAlchemy 查询语言。由于我的 SQL 查询中有很多重复,我很确定我可以使用 SQLAlchemy 做一些更智能的事情而无需重复。

我无法使用 SQLAlchemy 在“ORDER BY”中执行子查询。

有人有想法吗?

谢谢你, 埃德

编辑:

我的第一个想法是做类似的事情:

 Security.query.join(Valuation)\
      .filter(
          Valuation.volume != None,
          Valuation.px_close != None,
          Valuation.date <= date(2015, 05, 23),
          Valuation.date > date(2015, 05, 16)
      ).order_by(
          db.session.query(Valuation.volume).filter(
              Valuation.volume != None,
              Valuation.px_close != None,
              Valuation.date <= date(2015, 05, 23),
              Valuation.date > date(2015, 05, 16)
          ).desc()
      )

但是:

我有一个关于 desc() 的错误:“AttributeError: 'BaseQuery' 对象没有属性 'desc'” 我在子查询中没有链接“WHERE v.security_id = s.id”,因为我无权访问子查询中的“安全”

【问题讨论】:

【参考方案1】:

我终于找到了解决办法:

Security.query.join(Valuation)\
        .filter(tuple_(Valuation.security_id, Valuation.date).in_(
            db.session.query(Valuation.stock_id, func.max(Valuation.date))\
            .filter(Valuation.volume != None,
                    Valuation.px_close != None,
                    Valuation.date > date(2015, 05, 16),
                    Valuation.date <= date(2015, 05, 23))
            .group_by(Valuation.security_id)))
            .order_by(Valuation.volume.desc())
            .limit(10)
            .all()

产生:

SELECT security.*
FROM security
    JOIN valuation ON security.id = valuation.security_id
    WHERE (valuation.security_id, valuation.date) IN (
        SELECT valuation.security_id, max(valuation.date)
        FROM valuation
        WHERE valuation.volume IS NOT NULL
            AND valuation.px_close IS NOT NULL
            AND valuation.date > "2015-05-16"
            AND valuation.date <= "2015-05-23"
            GROUP BY valuation.security_id
    )
ORDER BY valuation.volume DESC
LIMIT 10

哪个效率更高,不需要重复。

【讨论】:

以上是关于ORDER BY SQLAlchemy 中的子查询的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Flask-SQLAlchemy 中执行多个“order_by”?

Flask 学习-97.Flask-SQLAlchemy 排序 order_by()

SQLAlchemy(四):SQLAlchemy查询高级

在 JPA Criteria API 的子查询中使用 ORDER BY 的替代方法是啥?

按 sqlalchemy 中的连接别名排序

如何使用在SQLAlchemy和contains_eager中的关系上定义的order_by?