【问题标题】:SQLAlchemy func.count on boolean column布尔列上的 SQLAlchemy func.count
【发布时间】:2016-09-16 15:53:01
【问题描述】:

如何轻松计算特定列为 true 的行数以及它为 false 的行数?

我不能(或者我可以吗?)使用 count() 运行查询,因为我将此计数嵌入到 having() 子句中,例如:

.having(func.count(Question.accepted) >
        func.count(not_(Question.accepted)))

但是通过上述方式,函数计算不等式两边的每一行。

我试过这样的

.having(func.count(func.if_(Question.accepted, 1, 0)) >
        func.count(func.if_(Question.accepted, 0, 1)))

但我得到一个错误

函数 if(boolean, integer, integer) 不存在

(好像postgresql中不存在)。

如何轻松统计column为true和false的行数?

【问题讨论】:

    标签: python postgresql sqlalchemy


    【解决方案1】:

    HAVING clause 中使用聚合函数非常合法,因为HAVING 消除了组行。条件计数可以通过使用NULLs 不计数的属性来实现:

    count(expression) ...表达式的值不为空的输入行数

    或者如果使用PostgreSQL 9.4 or later,则使用aggregate FILTER clause

    count(*) FILTER (WHERE something > 0)
    

    您也可以使用sum of ones (and zeros)

    PostgreSQL >= 9.4 和 SQLAlchemy >= 1.0.0

    使用filtered aggregate function:

    .having(func.count(1).filter(Question.accepted) >
            func.count(1).filter(not_(Question.accepted)))
    

    旧版 PostgreSQL 和/或 SQLAlchemy

    ​​>

    “if”的 SQL 模拟是 CASE expression 或在本例中为 nullif() 函数。两者可以一起使用,NULLs 不算:

    from sqlalchemy import case
    
    ...
    
    .having(func.count(case([(Question.accepted, 1)])) >
            func.count(case([(not_(Question.accepted), 1)])))
    

    或:

    .having(func.count(func.nullif(Question.accepted, False)) >
            func.count(func.nullif(Question.accepted, True)))
    

    使用nullif() 可能会有点混乱,因为“条件”是您不想想要计算的。您可以设计一个使条件更自然的表达式,但这留给读者。这两个是更便携的解决方案,但另一方面,FILTER 子句是标准的,虽然没有广泛使用。

    【讨论】:

      【解决方案2】:

      你可以使用这个查询:

      Session.query(func.sum(case([(Question.accepted == True, 1)], else_=0).label('accepted_number'))
      

      同一列将用于 False 值,但条件为 False

      或者,您可以使用窗口功能:

      Session.query(func.count(Question.id).over(partition_by=Question.accepted), Question.accepted).all()
      

      结果将包含两行(如果 Question.accepted 中只有两个可能的值),其中第一列是值的数量,第二列是“接受”列的值。

      【讨论】:

        【解决方案3】:

        大多数类型的 SQL 将布尔值存储为 1 位整数,并且对它们正确调用 SUM 可为您提供正布尔值/真行数。在你的情况下,你可以简单地做:

        func.sum(Question.accepted)
        

        如果您的 SQL 引擎将布尔值的 SUM 作为布尔值返回(不确定是否有),则可以使用 expression function cast() 将其强制为整数,而不是使用 func.count(case([(Question.accepted, 1)]))

        func.sum(sqlalchemy.cast(Question.accepted, sqlalchemy.Integer))
        

        最后,如果您希望在 Python 端将总和作为整数,expression function type_coerce() 可以解决问题:

        sqlalchemy.type_coerce(func.sum(Question.accepted), sqlalchemy.Integer)
        

        【讨论】:

        • 什么 SQL “开销”?这似乎应该解释得更多。请注意,SUM() 技巧在“您也可以使用一(和零)的总和”中被提及并链接到该技巧。 SUM() 技巧有一个优势:它为 0 行产生不同的结果。
        • 我的错,我原以为case 语句会在 SQL 中增加一些处理时间,但在 MySQL 中进行一些测试后,似乎影响可以忽略不计。尽管如此,我仍然认为当布尔值的总和工作得很好并且可以通过type_coerce 获得时,不要用无用的case 语句弄乱代码会更干净。
        • 同意,整个COUNT(CASE ... ) 的事情在眼睛上并不容易。虽然我个人非常喜欢<aggregate> FILTER (WHERE <predicate>),但它相当灵活,虽然没有得到广泛支持。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-08-09
        • 2012-08-16
        • 2021-05-17
        • 1970-01-01
        • 2020-10-14
        • 1970-01-01
        • 2023-03-16
        相关资源
        最近更新 更多