SQLAlchemy - 在查询中过滤func.count
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQLAlchemy - 在查询中过滤func.count相关的知识,希望对你有一定的参考价值。
假设我有一个包含列的表,它有一些整数值,我想计算该列超过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语句中进行过滤,并且我无法在查询结束时应用过滤器,因为如果我在结尾处应用> 200约束,则total_count将不起作用。
使用RAW SQL也是一种选择,它不必是Sqlalchemy
答案
不幸的是,MariaDB不支持aggregate FILTER clause,但你可以解决using a CASE expression或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)
is 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方言创建一个compilation extension,它将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
以上是关于SQLAlchemy - 在查询中过滤func.count的主要内容,如果未能解决你的问题,请参考以下文章
如何在 sqlalchemy 查询过滤器中使用用户定义的 python 函数?
从列表中过滤为查询字符串 postgresql flask sqlalchemy [重复]