python拼接sql?duckdb:不允许你用这么low的方式

我是数据外星人 2024-03-28 09:14:33

duckdb 是 python 中高性能分析型数据库,它里面有一套很神秘的"关系" 和 表达式函数。今天我们来盘一盘。

事情源自于一位小伙伴,它给了我一个使用 duckdb 的例子代码:

为什么执行会报错?很显然,单引号的问题,如果里面换成两个就可以表达一个单引号

我明明就希望查询的是一个单引号的内容,却要这么写,多麻烦。

这一切的问题,全是把 sql 当作普通的文本拼接导致。

解决方法有许多。第一种是所有数据库引擎都有提供的参数化查询:

行1:注意查询内容里面只需要原文编写即可

参数化查询的好处在于,它会自行判断数据类型,类似文本需要双引号包围的问题,我们不需要操心。

不过,在 duckdb 中,使用 execute 才能使用参数化,并且要额外调用 fetch 相关方法才能得到结果。

但我更喜欢使用 query 方法,那怎么办?

duckdb 有自身实现的 sql 解析引擎。平时我们编写的 sql 文本,duckdb 会解析编译成 sql 表达式。而 duckdb 在 python 端公开了这些表达式的接口。

像这里的例子,在 sql 表达中,其实就是定义了一个常量。

行1:通过 duckdb.ConstantExpression 把内容传进去,就得到一个表达式。

其实,这玩意就是前面说到的参数化的操作。

duckdb 还内置了其他逻辑的表达式。比如:

可以看到,这些表达式对象可以覆盖几乎所有的 sql 逻辑。

当你需要动态构建各种表达式的时候,这些都是非常实用的方法。

细心的你可能会发现,query 方法返回的结果,能直接打印数据,但是结果却不是任何有效的数据类型,比如 dataFrame 等。

这就要说到 duckdb 中最有趣的"关系"函数。它可以实现类似 polars 的延迟执行的强大效果

正常使用 duckdb 加载数据,一般是:

本身提供了许多 from 相关方法

加载之后,得到的是一个叫 DuckDBPyRelation 对象。

我们可以利用它,把一个 sql 查询,拆分成一层层进行构建。比如,

等价于:

可以看到,其实关系函数构造的查询更加简洁。

当然,普通场景不会使用这种关系函数构造查询。它主要用于用户界面动态构造查询的场景。

那为什么说它是延迟执行呢?

如果我们把上面例子中的每一步都用单独的变量"拿住",但不打印:

不管数据有多少,它们都没有实际执行查询。直到你让某个关系对象输出。

在 jupyter notebook 环境,把关系对象放在单元格最后,则视为打印输出

比如,我们希望输出 g1 结果到 dataframe:

此时才会真正执行关系对象"积累" 下来的逻辑(上面逻辑中的 第一次 filter + 分组计算)

它们类似数据库的虚拟视图

值得一提的是,许多关系方法的参数,都可以传入前面说到的"表达式"对象。它们会经常被组合应用。

不要忘记一键三连。你的点赞、收藏、关注,是我创作的动力。

1 阅读:239

我是数据外星人

简介:感谢大家的关注