Mat*_*Som 5 python mysql sql orm sqlalchemy
我有这样的疑问:
SELECT COUNT(DISTINCT Serial, DatumOrig, Glucose) FROM values;
Run Code Online (Sandbox Code Playgroud)
我尝试用SQLAlchemy这种方式重新创建它:
session.query(Value.Serial, Value.DatumOrig, Value.Glucose).distinct().count()
Run Code Online (Sandbox Code Playgroud)
但这转化为:
SELECT count(*) AS count_1
FROM (SELECT DISTINCT
values.`Serial` AS `values_Serial`,
values.`DatumOrig` AS `values_DatumOrig`,
values.`Glucose` AS `values_Glucose`
FROM values)
AS anon_1
Run Code Online (Sandbox Code Playgroud)
它不会内联调用 count 函数,而是将select unique包装到子查询中。
我的问题是: SQLAlchemy 有哪些不同的方法来计算多列上的不同选择以及它们会转换成什么?
有什么解决方案可以转化为我原来的查询吗?性能或内存使用情况有什么严重差异吗?
首先,我认为COUNT(DISTINCT)支持多个表达式是 MySQL 的一个扩展。例如,您可以在 PostgreSQL 中使用ROW值实现相同的目的,但对于 NULL 的行为不同。在 MySQL 中,如果任何值表达式的计算结果为 NULL,则该行不合格。这也导致了问题中两个查询之间的不同:
Serial如果查询中、DatumOrig、 或中的任何一个Glucose为 NULL COUNT(DISTINCT),则该行不符合条件,或者换句话说,不计数。COUNT(*)是子查询的基数anon_1,或者换句话说是行数。SELECT DISTINCT Serial, DatumOrig, Glucose将包含(不同的)带有 NULL 的行。查看EXPLAIN2 个查询的输出,看起来子查询导致 MySQL 使用临时表。这可能会导致性能差异,尤其是在磁盘上实现时。
在 SQLAlchemy 中生成多值COUNT(DISTINCT)查询有点棘手,因为count()它是一个通用函数并且实现更接近 SQL 标准。它只接受单个表达式作为其(可选)位置参数,这同样适用于distinct(). 如果所有其他方法都失败了,您可以随时恢复到text()片段,就像在这种情况下一样:
# NOTE: text() fragments are included in the query as is, so if the text originates
# from an untrusted source, the query cannot be trusted.
session.query(func.count(distinct(text("`Serial`, `DatumOrig`, `Glucose`")))).\
select_from(Value).\
scalar()
Run Code Online (Sandbox Code Playgroud)
这远不是可读和可维护的代码,但现在已经完成了工作。另一种选择是编写一个实现 MySQL 扩展的自定义构造,或者按照您的尝试重写查询。形成生成所需 SQL 的自定义构造的一种方法是:
from itertools import count
from sqlalchemy import func, distinct as _distinct
def _comma_list(exprs):
# NOTE: Magic number alert, the precedence value must be large enough to avoid
# producing parentheses around the "comma list" when passed to distinct()
ps = count(10 + len(exprs), -1)
exprs = iter(exprs)
cl = next(exprs)
for p, e in zip(ps, exprs):
cl = cl.op(',', precedence=p)(e)
return cl
def distinct(*exprs):
return _distinct(_comma_list(exprs))
session.query(func.count(distinct(
Value.Serial, Value.DatumOrig, Value.Glucose))).scalar()
Run Code Online (Sandbox Code Playgroud)