Pivot 内部实现
PIVOT
Pivoting 通过 SQL 重写与专用 PhysicalPivot 算子组合实现,以获得更高性能。
每个 PIVOT 会先被实现为“聚合到列表”,再由 PhysicalPivot 将这些列表转换为最终列名与列值。
若 pivot 目标列需动态检测(即未使用 IN 子句),还需要额外预处理步骤。
Goose 与大多数 SQL 引擎一样,要求在查询开始时已知所有列名与类型。
为自动检测 PIVOT 结果列,语句会被拆解并重写为多个查询。
系统会使用 ENUM 类型 找到应成为列名的 distinct 值,
再把这些 ENUM 注入到 PIVOT 的 IN 子句中。
当 IN 子句填充完成后,查询会再次被重写为“聚合到列表”的形式。
例如:
PIVOT cities
ON year
USING sum(population);
首先会被翻译为:
CREATE TEMPORARY TYPE __pivot_enum_0_0 AS ENUM (
SELECT DISTINCT
year::VARCHAR
FROM cities
ORDER BY
year
);
PIVOT cities
ON year IN __pivot_enum_0_0
USING sum(population);
最后翻译为:
SELECT country, name, list(year), list(population_sum)
FROM (
SELECT country, name, year, sum(population) AS population_sum
FROM cities
GROUP BY ALL
)
GROUP BY ALL;
该步骤会得到如下结果:
| country | name | list("year") | list(population_sum) |
|---|---|---|---|
| NL | Amsterdam | [2000, 2010, 2020] | [1005, 1065, 1158] |
| US | Seattle | [2000, 2010, 2020] | [564, 608, 738] |
| US | New York City | [2000, 2010, 2020] | [8015, 8175, 8772] |
随后 PhysicalPivot 算子会把这些列表转成列名与列值,返回如下结果:
| country | name | 2000 | 2010 | 2020 |
|---|---|---|---|---|
| NL | Amsterdam | 1005 | 1065 | 1158 |
| US | Seattle | 564 | 608 | 738 |
| US | New York City | 8015 | 8175 | 8772 |
UNPIVOT
内部实现
Unpivot 全部通过 SQL 重写实现。
每个 UNPIVOT 会被重写为一组 unnest 函数,分别作用于“列名列表”和“列值列表”。
若是动态 unpivot,会先求值 COLUMNS 表达式得到列列表。
例如:
UNPIVOT monthly_sales
ON jan, feb, mar, apr, may, jun
INTO
NAME month
VALUE sales;
会被翻译为:
SELECT
empid,
dept,
unnest(['jan', 'feb', 'mar', 'apr', 'may', 'jun']) AS month,
unnest(["jan", "feb", "mar", "apr", "may", "jun"]) AS sales
FROM monthly_sales;
注意:单引号用于构造文本列表以填充 month,双引号用于读取列值填充 sales。
其结果与初始示例一致:
| empid | dept | month | sales |
|---|---|---|---|
| 1 | electronics | jan | 1 |
| 1 | electronics | feb | 2 |
| 1 | electronics | mar | 3 |
| 1 | electronics | apr | 4 |
| 1 | electronics | may | 5 |
| 1 | electronics | jun | 6 |
| 2 | clothes | jan | 10 |
| 2 | clothes | feb | 20 |
| 2 | clothes | mar | 30 |
| 2 | clothes | apr | 40 |
| 2 | clothes | may | 50 |
| 2 | clothes | jun | 60 |
| 3 | cars | jan | 100 |
| 3 | cars | feb | 200 |
| 3 | cars | mar | 300 |
| 3 | cars | apr | 400 |
| 3 | cars | may | 500 |
| 3 | cars | jun | 600 |