簡體   English   中英

SQLAlchemy-在查詢中過濾func.count

[英]SQLAlchemy - filtering func.count within query

假設我有一個帶有列的表,該表具有一些整數值,並且我想計算該列的值超過200的百分比。

這是一個關鍵,如果我可以在可以使用group_by的一個查詢中執行此操作,則希望這樣做。

results = db.session.query(
        ClassA.some_variable,
        label('entries', func.count(ClassA.some_variable)),
        label('percent', *no clue*)
  ).filter(ClassA.value.isnot(None)).group_by(ClassA.some_variable)

或者,也可以考慮不喜歡在客戶端進行百分比計算,就像這樣。

results = db.session.query(
        ClassA.some_variable,
        label('entries', func.count(ClassA.some_variable)),
        label('total_count', func.count(ClassA.value)),
        label('over_200_count', func.count(ClassA.value > 200)),
  ).filter(ClassA.value.isnot(None)).group_by(ClassA.some_variable)

但是我顯然不能在count statemenet內進行過濾,也無法在查詢的末尾應用過濾器,因為如果在末尾應用> 200約束,total_count將無法工作。

也可以使用RAW SQL,它不一定是Sqlalchemy

不幸的是,MariaDB不支持聚合FILTER子句 ,但是您可以使用CASE表達式NULLIF解決該問題,因為COUNT返回給定表達式的非空值的計數:

from sqlalchemy import case

...

func.count(case([(ClassA.value > 200, 1)])).label('over_200_count')

考慮到這一點,您可以簡單地計算百分比

(func.count(case([(ClassA.value > 200, 1)])) * 1.0 /
 func.count(ClassA.value)).label('percent')

盡管有一個優勢:如果func.count(ClassA.value) 0怎么辦? 根據您是將0還是NULL視為有效的返回值,您可以使用另一個CASE表達式或NULLIF:

dividend = func.count(case([(ClassA.value > 200, 1)])) * 1.0
divisor = func.count(ClassA.value)

# Zero
case([(divisor == 0, 0)],
     else_=dividend / divisor).label('percent')

# NULL
(dividend / func.nullif(divisor, 0)).label('percent')

最后,您可以為mysql方言創建一個編譯擴展 ,該擴展將FILTER子句重寫為合適的CASE表達式:

from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import FunctionFilter
from sqlalchemy.sql.functions import Function
from sqlalchemy import case


@compiles(FunctionFilter, 'mysql')
def compile_functionfilter_mysql(element, compiler, **kwgs):
    # Support unary functions only
    arg0, = element.func.clauses

    new_func = Function(
        element.func.name,
        case([(element.criterion, arg0)]),
        packagenames=element.func.packagenames,
        type_=element.func.type,
        bind=element.func._bind)

    return new_func._compiler_dispatch(compiler, **kwgs)

有了這一點,您可以將股息表示為

dividend = func.count(1).filter(ClassA.value > 200) * 1.0

編譯成

In [28]: print(dividend.compile(dialect=mysql.dialect()))
count(CASE WHEN (class_a.value > %s) THEN %s END) * %s

暫無
暫無

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

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