SQLAlchemy func.count en columna booleana

¿Cómo puedo contar fácilmente el número de filas donde una columna particular es true y el número donde es false ?

No puedo (¿o puedo?) Ejecutar la consulta con count () porque estoy incrustando este conteo en una cláusula having (), como:

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

pero con la forma anterior, la función cuenta cada línea en ambos lados de la desigualdad.

Probé algo como esto

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

Pero me sale un error

función si (booleano, entero, entero) no existe

(Parece que no existe en postgresql).

¿Cómo puedo contar fácilmente el número de filas donde la columna es verdadera y falsa?

El uso de funciones agregadas en una cláusula HAVING es muy legal, ya que HAVING elimina las filas de grupo. El conteo condicional se puede lograr usando la propiedad que NULL s no cuenta:

count(expression) … número de filas de entrada para las cuales el valor de expresión no es nulo

o si usa PostgreSQL 9.4 o posterior , con la cláusula FILTER agregada :

 count(*) FILTER (WHERE something > 0) 

También puedes usar una sum de unos (y ceros) .

PostgreSQL> = 9.4 y SQLAlchemy> = 1.0.0

Usando una función agregada filtrada :

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

PostgreSQL y / o SQLAlchemy más antiguos

El análogo de SQL para “if” es una expresión CASE o, en este caso, la función nullif() . Ambos pueden usarse junto con el hecho de que los NULL s no cuentan:

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

o:

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

Usar nullif() puede ser un poco confuso ya que la “condición” es lo que no quiere contar. Podría usar una expresión que haría la condición más natural, pero eso queda para el lector. Estas 2 son soluciones más portátiles, pero, por otro lado, la cláusula FILTER es estándar, aunque no está ampliamente disponible.

Puede utilizar esta consulta:

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

Y la misma columna será para el valor Falso, pero con la condición Falso

O bien, puede utilizar la función de ventana:

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

El resultado contendrá dos filas (si solo hay dos valores posibles en Question.accepted), donde la primera columna es el número de valores y la segunda los valores de la columna “aceptada”.