当ClickHouse查询返回零行时,如何仍然保留列结构?
我在用一个用Python编写的分析工具,它会查询ClickHouse数据库。遇到的问题是,当查询返回零行时,结果有时也会失去列结构,最终得到一个形状为 (0, 0) 的DataFrame,而不是具有预期列的那个。这会导致错误,因为即使没有数据,我也依赖于列名的存在。
我使用官方的clickhouse-connect Python库来执行如下查询:
result = self.conn.query(query)
df = pd.DataFrame(result.result_rows, columns=result.column_names)
在大多数情况下这都能正常工作。然而,对于某些返回零行的查询,result.column_names也会为空。
在逐步排查后,我发现是我更大查询中的一个具体的CTE(unconnected_pids)参与其中。当这部分没有返回行时,最终结果就没有任何列元数据。
unconnected_pids AS (
SELECT sc.machine_id AS machine_id, sc.pid AS pid, sc.service_name AS service_name
FROM service_context sc
GLOBAL LEFT JOIN pid_connections pc
ON (sc.machine_id = pc.machine1 AND sc.pid = pc.pid1)
OR (sc.machine_id = pc.machine2 AND sc.pid = pc.pid2)
),
missing_discovery AS (
SELECT
ps.machine_id AS machine_id,
ps.pid AS pid,
ps.inode_id,
si.dst_address AS dst_address,
si.dst_port
FROM pid_sock ps
GLOBAL LEFT JOIN socket_context sc ON sc.machine_id = ps.machine_id AND sc.inode_id = ps.inode_id
GLOBAL LEFT JOIN linux_consts proto
ON sc.protocol = proto.value AND proto.const_type = 'family_protocol'
GLOBAL LEFT JOIN tcp_sock_map tsm
ON (ps.machine_id = tsm.machine1 AND ps.inode_id = tsm.sock1)
OR (ps.machine_id = tsm.machine2 AND ps.inode_id = tsm.sock2)
GLOBAL LEFT JOIN socket_inet si ON si.machine_id = ps.machine_id AND si.inode_id = ps.inode_id
WHERE
proto.const_name = 'IPPROTO_TCP' AND tsm.machine1 IS NULL AND si.dst_port <> 0
)
SELECT
machine_id, pid, dst_address, COUNT(*) AS connections
FROM missing_discovery
GROUP BY 1, 2, 3
这段查询是一个更大型的分析系统的一部分,最初是基于DuckDB构建的。在DuckDB那边,这个逻辑也能正常工作,因为它会保留模式。
让人有点困惑的是,行为取决于查询结果如何变为零行。如果我取一个我确定结果会有行的查询,然后用 LIMIT 0 限制输出,列结构就会被保留。然而,当查询因为数据原因(例如通过join和过滤把所有行都消除)而成为零行时,结果可能完全失去其模式。
ClickHouse的日志没有显示错误,较简单的查询也按预期工作。
我发现的变通方法:
- 可以使用clickhouse-driver库(它有一个
query_dataframe()函数可以正确返回空的DataFrame) - 传入预期的列名,并编写一个包装函数,当查询结果shape评估为 (0,0) 时返回具有正确列的DataFrame
解决方案
要回答在查询结果为空时如何保持表结构这个最终问题,似乎做不到,至少就官方的clickhouse-connect库而言。不过Clickhouse-driver库在它们的 query_dataframe() 方法中在这次提交中修复了这个问题:
https://github.com/mymarilyn/clickhouse-driver/commit/4bc8a460da6163549f3c48a3d33d2610fad52e43
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。