SqlAlchemy:多列分别计数的去重行数

32

我做不到:

>>> session.query(
        func.count(distinct(Hit.ip_address, Hit.user_agent)).first()
TypeError: distinct() takes exactly 1 argument (2 given)

我可以做:

session.query(
        func.count(distinct(func.concat(Hit.ip_address, Hit.user_agent))).first()

在一个名为“页面加载”(pageload)的数据库表中,唯一用户数量计数是没问题的。

但对于一般情况,这并不正确。例如,下面的表会得到1而不是2:

 col_a | col_b
----------------
  xx   |  yy
  xxy  |  y

有没有办法生成以下SQL语句(至少在PostgreSQL中是有效的)?

SELECT count(distinct (col_a, col_b)) FROM my_table;
4个回答

25

distinct()在附加到查询对象时可以接受多个参数:

session.query(Hit).distinct(Hit.ip_address, Hit.user_agent).count()

它应该生成类似于:

SELECT count(*) AS count_1
FROM (SELECT DISTINCT ON (hit.ip_address, hit.user_agent)
hit.ip_address AS hit_ip_address, hit.user_agent AS hit_user_agent
FROM hit) AS anon_1

这甚至更接近于你想要的。


3
这使得我在所有列上都有一个独特的选择,不仅仅是作为参数添加的那些列。 - MattSom
你不需要查询(Hit),相反你需要查询(Hit.ip_address, Hit.user_agent),这样SQLA才能正确处理它。 - varela

25

可以使用tuple_()结构来生成精确的查询:

session.query(
    func.count(distinct(tuple_(Hit.ip_address, Hit.user_agent)))).scalar()

10

看起来sqlalchemy的distinct()只接受一个列或表达式。

另一种解决方法是使用group_bycount。这应该比使用两个列的concat更有效 - 使用group by,如果索引存在,则数据库将能够使用它们:

session.query(Hit.ip_address, Hit.user_agent).\
    group_by(Hit.ip_address, Hit.user_agent).count()

生成的查询仍然与您所询问的不同:

SELECT count(*) AS count_1 
FROM (SELECT hittable.user_agent AS hittableuser_agent, hittable.ip_address AS sometable_column2 
FROM hittable GROUP BY hittable.user_agent, hittable.ip_address) AS anon_1

非常好。我从没想过这种方法,因为在SQL中需要输入很多内容...但在SQLA中,这非常容易! - EoghanM

0

您可以在concat函数中添加一些变量或字符,以使其不同。以您的示例为参考,应该是:

session.query(
  func.count(distinct(func.concat(Hit.ip_address, "-", Hit.user_agent))).first()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接