在 pypika
中自定义 SQL 函数¶
pypika
是一个在 Python 中构建 SQL 语句的工具。这篇文章提供了一个很棒的入门教程,介绍了许多 pypika
的基本用法。
本文以 ClickHouse 中的 quantileExact
函数为例,介绍如何在 pypika
中自定义函数,尤其是自定义字符串拼接方式的实现方法。
问题背景¶
假设我们在 ClickHouse 数据库中有一个表格 my_table
,其中包含每一天的每一笔账单金额。我们需要计算每一天的所有单笔账单金额的 75 分位数,可以构造如下 SQL 语句:
其中,quantileExact
是计算分位数的函数,详见文档。
失败方法¶
首先,定义 CustomFunction
,将 ["quantile", "value"]
作为参数。
Python
import sqlparse
from pygments import highlight
from pygments.formatters import TerminalFormatter
from pygments.lexers import SqlLexer
from pypika import Field, Query, Table
from pypika import CustomFunction
QuantileExact = CustomFunction("quantileExact", ["quantile", "value"])
# 定义表
my_table = Table("my_table")
# 定义查询
query = (
Query.from_(my_table)
.select(
my_table.date,
QuantileExact(0.75, Field("amount")).as_("amount_75th_percentile"),
)
.groupby(my_table.date)
)
# 打印生成的 SQL 查询
print(
highlight(
sqlparse.format(
query.get_sql(quote_char=None),
reindent=True,
keyword_case="upper",
),
SqlLexer(),
TerminalFormatter(),
)
)
但是,得到的 quantileExact
是将两个参数放在一个括号中,即 quantileExact(0.75, amount)
。这并不是 ClickHouse 中的 quantileExact(0.75)("amount")
调用用法。
重写 get_function_sql
方法¶
Python
import sqlparse
from pygments import highlight
from pygments.formatters import TerminalFormatter
from pygments.lexers import SqlLexer
from pypika import Field, Query, Table
from pypika.terms import Function
class QuantileExact(Function):
def __init__(self, quantile, field, *args, **kwargs):
super(QuantileExact, self).__init__("quantileExact", *args, **kwargs)
self.quantile = quantile
self.field = field
def get_function_sql(self, **kwargs) -> str:
return "{name}({quantile})({field})".format(
name=self.name,
quantile=self.quantile,
field=self.field,
)
# 定义表
my_table = Table("my_table")
# 定义查询
query = (
Query.from_(my_table)
.select(
my_table.date,
QuantileExact(0.75, Field("amount")).as_("amount_75th_percentile"),
)
.groupby(my_table.date)
)
# 打印生成的 SQL 查询
print(
highlight(
sqlparse.format(
query.get_sql(quote_char=None),
reindent=True,
keyword_case="upper",
),
SqlLexer(),
TerminalFormatter(),
)
)
我们重写了 get_function_sql
方法,将其变成 "{name}({quantile})({field})"
的形式。这样,QuantileExact(0.75, Field("amount"))
就能得到 quantileExact(0.75)("amount")
的 SQL 语句,从而被 ClickHouse 正确识别。