FlatQL:一种扁平化查询语言

设计 FlatQL 的初衷是通过一种扁平化的查询语言,以可视化的方式构建复杂查询,从而使得数据分析过程中可以减少对数据工程师的依赖。在传统的数据分析中, SQL 通常通过模板语言的形式实现动态查询,即预先定义一个完整的 SQL 查询,并通过动态替换其中的部分内容来实现某种程度的灵活性 (例如:Superset 的模板化 SQL)。然而,这种模板化的方式仅能满足部分简单查询的动态需求。当动态投影或过滤条件对整个 SQL 结构产生影响时, 模板化语言便显得力不从心。例如:

  • 新的投影(新增加的维度或指标)需要构建一个子查询
  • 新增的过滤条件需要 Join 其它的表

在面对这些复杂场景时,模板化 SQL 无法提供足够的灵活性。常见的解决方案是通过预计算构建多层数据模型,即在原始数据模型之上, 构建一系列针对每个分析主题的数据模型。这样,在实际分析时,只需面向单表进行查询,并通过模板语言生成动态查询。然而,这种方式会带来巨大的数据建模成本, 且当分析需求发生变化时,需要重构数据模型,不仅影响效率,还会导致大量重复开发的成本。

FlatQL

FlatQL 采用扁平化的语法结构,只包含投影、过滤、排序三大基本操作,剔除了传统 SQL 中冗杂的部分(包括:分组、多表连接、子查询等复杂逻辑), 所有 SQL 技术相关的处理均由 Agile Query SQL 编译器的算法计算自动生成。这样设计的优势在于,开发者只需关注查询的核心需求。 FlatQL 的完整语法定义如下(BNF 形式):

statement:
    select

select:
    SELECT expression [AS alias] [, expression [AS alias] ]* FROM schemaIdentifier
    [WHERE booleanExpression [AND booleanExpression]* ]
    [ORDER BY expression [ASC | DESC] [, expression [ASC | DESC]]* ]
    [LIMIT integer]

expression:
    column | functionCall | binaryExpression | caseWhenExpression | parenExpression

...

我们来看几个FlatQL 示例:

1) 统计每个客户的销售量和销售额

SELECT
    customers.company_name AS "客户",
    ROUND(SUM(order_details.quantity), 2) AS "销售量",
    ROUND(SUM(order_details.quantity * order_details.unit_price), 2) AS "销售额"
FROM "329875"

2) 统计销售额月增长率大于 10% 的那些品类的销售额

SELECT
    categories.category_name AS "品类",
    SUM(order_details.quantity * order_details.unit_price) AS "销售额"
FROM "329875"
WHERE
    GROWTH_OF(SUM(order_details.quantity * order_details.unit_price), orders.order_date) > 10

3) 统计饮料和食品的客户数量和订单数据大于 1,同时销售额大于 100 的客户数量

SELECT
    categories.category_name AS "品类",
    COUNT(customers.company_name) AS "客户数量",
    COUNT_IF(
        GROUP_COUNT(orders.order_id, customers.customer_id) > 1
            AND GROUP_SUM(order_details.quantity * order_details.unit_price, customers.customer_id) > 100,
        customers.customer_id
    ) AS "高级客户数量"
FROM "329875"
WHERE categories.category_name IN ('饮料', '食品')  LIMIT 1000

4) 统计每个品类的复购率(计算复购率的数据使用去年的数据),并以销售额升序排序

SELECT
    categories.category_name AS "品类",
    COUNT_IF(GROUP_COUNT(orders.order_id, customers.customer_id, orders.order_date = LAST_YEARS(1)) > 1)
        / TO_FLOAT(COUNT(customers.customer_id)) * 100 AS "复购率"
FROM "329875"
ORDER BY SUM(order_details.quantity * order_details.unit_price) ASC LIMIT 1000

5)统计去年每个品类的月均销售额

SELECT
    categories.category_name AS "品类",
    AVG(
        GROUP_SUM(order_details.quantity * order_details.unit_price, TO_MONTH(orders.order_date))
    ) AS "月均销售额"
FROM "329875"
WHERE orders.order_date = LAST_YEAR(1)  LIMIT 1000

从上述示例中可以看出,FlatQL 的主要特点:

  • 整体语法设计与标准 SQL 保持高度相似,确保用户在使用时能够轻松上手,同时减少学习成本。
  • 在查询涉及多张表的场景中,无需显式生成 JOIN 操作即可实现逻辑关联。
  • 在进行聚合值过滤时,可以直接在 WHERE 表达式中实现过滤,而无需使用 HAVING 子句。
  • 查询中所涉及的 GROUP_ 系列函数,在实际编译生成的 SQL 中会以子查询的形式存在。然而,在构建查询的过程中,用户无需关心子查询的具体实现细节, 也无需处理子查询与其他子查询之间的合并逻辑。这些繁琐的操作由 Agile Query 编译器自动完成,大幅降低了构建复杂查询的难度。

示例中的查询语句中的元素(投影、过滤、排序等)彼此独立,每个动态部分均由独立的维度或指标组成(包括过滤表达式,也仅为指标或维度拼接形成的布尔表达式)。 这种设计使得查询可以完全通过可视化方式轻松构建。整个查询中无需关注 SQL 编程中的逻辑关系,只需要关注单个指标的定义。

除了标准的聚合函数,FlatQL 还提供了大量领域相关函数,例如:

GROWTH_OF
LAST_YEAR
,其中标准聚合也与标准 SQL 中的标准聚合函数语义不同,例如:示例 5 中的,
AVG
函数内部嵌套了一个
SUM
函数。

可视化查询构建

可视化构建查询的前提是:1)查询语句中的各个元素相对独立,彼此之间不存在依赖关系;2)查询的逻辑结构简单,便于直观地进行可视化交互操作。 这样才能有效降低数据分析师的交互成本,无论查询的复杂度如何,交互形式不发生变化。Agile Query 的技术实现如下:

  1. 将维度和度量值转换为关键词:通过将数据库中的列或指标定义映射为关键词,在进行数据分析时,用户只需关注这些关键词,并可以灵活地进行组合。 这样的交互方式接近与使用自然语言进行数据分析,唯一不同的地方是 Agile Query 是精确的数据分析,而 NLP 形式的数据分析属于动态理解并构建查询。

  2. 可视化过滤和排序配置:由于 Agile Query 中的最小单元为列和指标定义,在前端可视化交互时,很容易将这个表达式定义组合成过滤表达式, 例如:拼接比较运算符

    orders.order_date = LAST_YEAR(1)
    , 或者
    GROWTH_OF(SUM(order_details.quantity * order_details.unit_price), orders.order_date) > 10
    这类形式。

总结

技术的发展主要体现在提高生产效率,释放更多生产力,从而投入到更复杂的工作中。Agile Query 释放的是数据工程的的开发工作量, 提升的是数据分析师获取数据的效率,从而提升企业整体的决策效率。Agile Query 还提供了丰富的可视化交互工具,让数据分析师能够自助构建查询, 让数据工程师可以专注于数据治理相关工作,不再因数据分析需求的变化而分心。

全天候售后服务
7x24小时专业工程师品质服务
极速服务应答
秒级应答为业务保驾护航
客户价值为先
从服务价值到创造客户价值
全方位安全保障
私有化部署,充分保证数据安全
©2022-2025 上海磊数信息技术有限公司
沪ICP备2024066390号-1