如何在Python的SQLite3中使用GROUP BY计算非空的行?

时间:2018-06-04 11:13:20

标签: python sqlite group-by

我在Python 3.6中使用标准的SQLite3。我想计算每个group_num有多少行有list_num的条目不是空的。空意为空字符串或NULL。

我目前分组:

SELECT group_num, count(list_num)  
FROM pair_candidates WHERE list_num IS NOT NULL 
GROUP BY group_num

这会计算有多少行具有相同的group_num,但不幸的是它没有告诉我这些行中有多少行list_num不是空的。

我尝试在GROUP BY子句之前添加WHERE list_num IS NOT NULL,但这没有帮助。在GROUP BY之后尝试HAVING list_num IS NOT NULL也没有帮助。

我需要做些什么来计算我的数量?

示例:

group_num | list_num
----------+---------
1         | 
1         | 1
2         | 1
2         | 1
3         | ""
3         |

我想要

group_num | count
----------+------
1         | 1
2         | 2
3         | 0

2 个答案:

答案 0 :(得分:1)

选择 list_num 不等于空字符串的行。因为与NULL的比较产生UNKNOWN,所以这些行也将被丢弃:

SELECT group_num, COUNT(*)
FROM pair_candidates
WHERE list_num != ''
GROUP BY group_num

但这不会产生没有符合条件的行的 group_num 的结果,因此结果并不完全符合您的预期。为了产生"空"组也可以使用COUNT(X)返回组中X不为NULL的次数这一事实。

SELECT group_num, COUNT(NULLIF(list_num, ''))
FROM pair_candidates
GROUP BY group_num
如果参数不同,

NULLIF(X, Y)将返回其第一个参数,如果它们相同则返回NULL,因此如果X为COUNT(NULLIF(X, '')),则''不计算。

一个完整的例子:

In [1]: from contextlib import closing

In [2]: import sqlite3

In [3]: conn = sqlite3.connect(':memory:')

In [4]: with closing(conn.cursor()) as cur:
   ...:     cur.execute('create table pair_candidates (group_num, list_num)')
   ...:     cur.executemany('insert into pair_candidates values (?, ?)', [
   ...:         (1, None), (1, 1), (2, 1), (2, 1), (3, ""), (3, None)])
   ...:         

In [5]: with closing(conn.cursor()) as cur:
   ...:     cur.execute('''select group_num, count(nullif(list_num, ''))
   ...:                    from pair_candidates
   ...:                    group by group_num''')
   ...:     res = cur.fetchall()
   ...:     

In [6]: res
Out[6]: [(1, 1), (2, 2), (3, 0)]

答案 1 :(得分:1)

AFAIK,您无法从SQL查询中获取它。 NULL是一种特殊的动物,只能被IS NULL捕获,绝对不能与""相同。但这不是全部:SELECT COUNT GROUP BY将永远不会返回0作为计数的行,但只会返回该组的无行。毕竟,该组的查询没有返回任何行,而count只是返回行上的聚合

当然可以使用外部联接在结果集中强制一行,但除非你真的精通SQL,否则它肯定会相当复杂。但是从Python代码混合2个查询是微不足道的:首先返回组列表并将计数初始化为0,第二个返回非零计数,并可用于更新初始映射。

假设con是与Sqlite3数据库的活动连接,您可以这样做:

groups = { x[0]: 0 for x in con.execute(
    "SELECT distinct group_num FROM pair_candidates").fetchall() }
groups.update({ x[0]: x[1] for x in con.execute(
    """SELECT group_num, count(list_num)
    FROM pair_candidates WHERE list_num != ""
    GROUP BY group_num""").fetchall() })
print(groups)

按预期返回:

{1: 1, 2: 2, 3: 0}