统计信息模式#
警告
本规范目前视为实验性标准。
原理#
统计信息对于快速查询处理非常有用。许多查询引擎使用统计信息来优化其查询计划。
Apache Arrow 格式本身不包含统计信息,但可以作为 Apache Arrow 数据读取的其他格式可能包含统计信息。例如,Apache Parquet C++ 实现可以读取 Apache Parquet 文件作为 Apache Arrow 数据,而该 Apache Parquet 文件可能包含统计信息。
为了便于交换,我们将统计信息的表示形式标准化为 Apache Arrow 数组。
用例#
以下是 Arrow C 流接口 的用例之一
模块 A 读取 Apache Parquet 文件并将其作为 Apache Arrow 数据。
模块 A 通过 Arrow C 流接口将读取到的 Apache Arrow 数据传递给模块 B。
模块 B 处理传递过来的 Apache Arrow 数据。
如果模块 A 能够将 Apache Parquet 文件关联的统计信息传递给模块 B,模块 B 就可以使用这些统计信息来优化其查询计划。
例如,DuckDB 使用了这种方法,但由于缺乏表示 Apache Arrow 数据统计信息的标准化方式,DuckDB 此前无法使用这些统计信息。
目标#
建立一种将统计信息表示为 Apache Arrow 数组的标准方式。
非目标#
建立一种传递表示统计信息的 Apache Arrow 数组的标准方式。
建立一种将统计信息嵌入到 Apache Arrow 数组本身的标准方式。
模式#
本规范仅提供统计信息的模式。这是将 Apache Arrow 数据集的统计信息表示为 Apache Arrow 数据的规范模式。
以下是统计信息模式的大纲
struct<
column: int32,
statistics: map<
key: dictionary<values: utf8, indices: int32>,
items: dense_union<...all needed types...>
>
>
以下是顶层 struct 的详细信息
姓名 |
数据类型 |
可为空 |
备注 |
|---|---|---|---|
|
|
|
以 0 为基准的列索引;如果统计信息描述的是整个表或记录批次(record batch),则为 null。 列索引的计算遵循与 RecordBatch 消息 相同的规则。 |
|
|
|
目标列、表或记录批次的统计信息。详细信息请参阅下方的独立表格。 |
以下是 statistics 中 map 的详细信息
键或项 |
数据类型 |
可为空 |
备注 |
|---|---|---|---|
key(键) |
|
|
字符串键即为统计信息的名称。由于同一统计信息可能在不同列中重复出现,因此使用字典编码以提高效率。精确统计值和近似统计值分配不同的键。每种统计信息均有下方说明。 |
items(项) |
|
|
统计信息的值为密集联合类型。根据键中的统计信息种类,它至少包含所有需要的类型。例如,当同时存在 密集联合数组虽然有字段名称,但我们未对其进行标准化,因为可以通过类型代码访问相应的字段。因此,可以使用任何有效的字段名称。 |
标准统计信息#
每种统计信息类型都有一个名称,作为每一列或整张表的统计信息映射中的键。dictionary<values: utf8, indices: int32> 用于对名称进行编码,以节省空间。
我们为同一统计信息的不同变体分配不同的名称,而不是使用标志位。例如,我们为“唯一值计数(distinct count)”统计信息的精确值和近似值分配不同的名称。
冒号 : 用作命名空间分隔符,类似于 自定义应用元数据。它可以在名称中多次使用。
ARROW 前缀是本规范当前及未来版本中预定义统计信息名称的保留命名空间。用户定义的统计信息不得使用此名称。例如,您可以使用您的产品名称作为命名空间,如 MY_PRODUCT:my_statistics:exact。
以下是预定义的统计信息名称
姓名 |
数据类型 |
备注 |
|---|---|---|
|
|
目标列中行的平均大小(以字节为单位)。(精确) |
|
|
目标列中行的平均大小(以字节为单位)。(近似) |
|
|
目标列中唯一值的数量。(精确) |
|
|
目标列中唯一值的数量。(近似) |
|
|
目标列中行的最大大小(以字节为单位)。(精确) |
|
|
目标列中行的最大大小(以字节为单位)。(近似) |
|
取决于目标 |
目标列中的最大值。(精确) |
|
取决于目标 |
目标列中的最大值。(近似) |
|
取决于目标 |
目标列中的最小值。(精确) |
|
取决于目标 |
目标列中的最小值。(近似) |
|
|
目标列中 null 值的数量。(精确) |
|
|
目标列中 null 值的数量。(近似) |
|
|
目标表、记录批次或数组中的行数。(精确) |
|
|
目标表、记录批次或数组中的行数。(近似) |
如果您发现某种可能对多个系统有用的统计信息,请在 Apache Arrow 开发邮件列表上提出建议。
当统计信息的生产者和消费者遵循预先约定的统计信息规范时,互操作性会得到提升。
示例#
以下是一些示例以供参考。
简单记录批次#
Schema
vendor_id: int32
passenger_count: int64
数据
vendor_id: [5, 1, 5, 1, 5]
passenger_count: [1, 1, 2, 0, null]
统计信息
目标 |
姓名 |
值 |
|---|---|---|
记录批次 |
行数 |
|
|
null 的数量 |
|
唯一值的数量 |
|
|
最大值 |
|
|
最小值 |
|
|
|
null 的数量 |
|
唯一值的数量 |
|
|
最大值 |
|
|
最小值 |
|
列索引
索引 |
目标 |
|---|---|
|
|
|
|
统计模式
struct<
column: int32,
statistics: map<
key: dictionary<values: utf8, indices: int32>,
items: dense_union<0: int64>
>
>
统计信息数组
column: [
null, # record batch
0, # vendor_id
1, # passenger_count
]
statistics:
offsets: [
0,
1, # record batch: 1 value: [0]
5, # vendor_id: 4 values: [1, 2, 3, 4]
9, # passenger_count: 4 values: [5, 6, 7, 8]
]
key:
values: [
"ARROW:row_count:exact",
"ARROW:null_count:exact",
"ARROW:distinct_count:exact",
"ARROW:max_value:exact",
"ARROW:min_value:exact",
]
indices: [
0, # "ARROW:row_count:exact"
1, # "ARROW:null_count:exact"
2, # "ARROW:distinct_count:exact"
3, # "ARROW:max_value:exact"
4, # "ARROW:min_value:exact"
1, # "ARROW:null_count:exact"
2, # "ARROW:distinct_count:exact"
3, # "ARROW:max_value:exact"
4, # "ARROW:min_value:exact"
]
items:
children:
0: [ # int64
5, # record batch: "ARROW:row_count:exact"
0, # vendor_id: "ARROW:null_count:exact"
2, # vendor_id: "ARROW:distinct_count:exact"
5, # vendor_id: "ARROW:max_value:exact"
1, # vendor_id: "ARROW:min_value:exact"
1, # passenger_count: "ARROW:null_count:exact"
3, # passenger_count: "ARROW:distinct_count:exact"
2, # passenger_count: "ARROW:max_value:exact"
0, # passenger_count: "ARROW:min_value:exact"
]
types: [ # all values are int64
0,
0,
0,
0,
0,
0,
0,
0,
0,
]
offsets: [
0,
1,
2,
3,
4,
5,
6,
7,
8,
]
复杂记录批次#
使用嵌套类型。
Schema
col1: struct<a: int32, b: list<item: int64>, c: float64>
col2: utf8
数据
col1: [
{a: 1, b: [20, 30, 40], c: 2.9},
{a: 2, b: null, c: -2.9},
{a: 3, b: [99], c: null},
]
col2: ["x", null, "z"]
统计信息
目标 |
姓名 |
值 |
|---|---|---|
记录批次 |
行数 |
|
|
null 的数量 |
|
|
null 的数量 |
|
唯一值的数量 |
|
|
近似最大值 |
|
|
近似最小值 |
|
|
|
null 的数量 |
|
|
最大值 |
|
最小值 |
|
|
|
null 的数量 |
|
近似最大值 |
|
|
近似最小值 |
|
|
|
null 的数量 |
|
唯一值的数量 |
|
列索引
索引 |
目标 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
请参阅 RecordBatch 消息 了解如何计算列索引。
统计模式
struct<
column: int32,
statistics: map<
key: dictionary<values: utf8, indices: int32>,
items: dense_union<
# For the number of rows, the number of nulls and so on.
0: int64,
# For the max/min values of col1.c.
1: float64
>
>
>
统计信息数组
column: [
null, # record batch
0, # col1
1, # col1.a
2, # col1.b
3, # col1.b.item
4, # col1.c
5, # col2
]
statistics:
offsets: [
0,
1, # record batch: 1 value: [0]
2, # col1: 1 value: [1]
6, # col1.a: 4 values: [2, 3, 4, 5]
7, # col1.b: 1 value: [6]
9, # col1.b.item: 2 values: [7, 8]
12, # col1.c: 3 values: [9, 10, 11]
14, # col2: 2 values: [12, 13]
]
key:
values: [
"ARROW:row_count:exact",
"ARROW:null_count:exact",
"ARROW:distinct_count:exact",
"ARROW:max_value:approximate",
"ARROW:min_value:approximate",
"ARROW:max_value:exact",
"ARROW:min_value:exact",
]
indices: [
0, # "ARROW:row_count:exact"
1, # "ARROW:null_count:exact"
1, # "ARROW:null_count:exact"
2, # "ARROW:distinct_count:exact"
3, # "ARROW:max_value:approximate"
4, # "ARROW:min_value:approximate"
1, # "ARROW:null_count:exact"
5, # "ARROW:max_value:exact"
6, # "ARROW:min_value:exact"
1, # "ARROW:null_count:exact"
3, # "ARROW:max_value:approximate"
4, # "ARROW:min_value:approximate"
1, # "ARROW:null_count:exact"
2, # "ARROW:distinct_count:exact"
]
items:
children:
0: [ # int64
3, # record batch: "ARROW:row_count:exact"
0, # col1: "ARROW:null_count:exact"
0, # col1.a: "ARROW:null_count:exact"
3, # col1.a: "ARROW:distinct_count:exact"
5, # col1.a: "ARROW:max_value:approximate"
0, # col1.a: "ARROW:min_value:approximate"
1, # col1.b: "ARROW:null_count:exact"
99, # col1.b.item: "ARROW:max_value:exact"
20, # col1.b.item: "ARROW:min_value:exact"
1, # col1.c: "ARROW:null_count:exact"
1, # col2: "ARROW:null_count:exact"
2, # col2: "ARROW:distinct_count:exact"
]
1: [ # float64
3.0, # col1.c: "ARROW:max_value:approximate"
-3.0, # col1.c: "ARROW:min_value:approximate"
]
types: [
0, # int64: record batch: "ARROW:row_count:exact"
0, # int64: col1: "ARROW:null_count:exact"
0, # int64: col1.a: "ARROW:null_count:exact"
0, # int64: col1.a: "ARROW:distinct_count:exact"
0, # int64: col1.a: "ARROW:max_value:approximate"
0, # int64: col1.a: "ARROW:min_value:approximate"
0, # int64: col1.b: "ARROW:null_count:exact"
0, # int64: col1.b.item: "ARROW:max_value:exact"
0, # int64: col1.b.item: "ARROW:min_value:exact"
0, # int64: col1.c: "ARROW:null_count:exact"
1, # float64: col1.c: "ARROW:max_value:approximate"
1, # float64: col1.c: "ARROW:min_value:approximate"
0, # int64: col2: "ARROW:null_count:exact"
0, # int64: col2: "ARROW:distinct_count:exact"
]
offsets: [
0, # int64: record batch: "ARROW:row_count:exact"
1, # int64: col1: "ARROW:null_count:exact"
2, # int64: col1.a: "ARROW:null_count:exact"
3, # int64: col1.a: "ARROW:distinct_count:exact"
4, # int64: col1.a: "ARROW:max_value:approximate"
5, # int64: col1.a: "ARROW:min_value:approximate"
6, # int64: col1.b: "ARROW:null_count:exact"
7, # int64: col1.b.item: "ARROW:max_value:exact"
8, # int64: col1.b.item: "ARROW:min_value:exact"
9, # int64: col1.c: "ARROW:null_count:exact"
0, # float64: col1.c: "ARROW:max_value:approximate"
1, # float64: col1.c: "ARROW:min_value:approximate"
10, # int64: col2: "ARROW:null_count:exact"
11, # int64: col2: "ARROW:distinct_count:exact"
]
简单数组#
Schema
int64
数据
[1, 1, 2, 0, null]
统计信息
目标 |
姓名 |
值 |
|---|---|---|
数组 |
行数 |
|
null 的数量 |
|
|
唯一值的数量 |
|
|
最大值 |
|
|
最小值 |
|
列索引
索引 |
目标 |
|---|---|
|
数组 |
统计模式
struct<
column: int32,
statistics: map<
key: dictionary<values: utf8, indices: int32>,
items: dense_union<0: int64>
>
>
统计信息数组
column: [
0, # array
]
statistics:
offsets: [
0,
5, # array: 5 values: [0, 1, 2, 3, 4]
]
key:
values: [
"ARROW:row_count:exact",
"ARROW:null_count:exact",
"ARROW:distinct_count:exact",
"ARROW:max_value:exact",
"ARROW:min_value:exact",
]
indices: [
0, # "ARROW:row_count:exact"
1, # "ARROW:null_count:exact"
2, # "ARROW:distinct_count:exact"
3, # "ARROW:max_value:exact"
4, # "ARROW:min_value:exact"
]
items:
children:
0: [ # int64
5, # array: "ARROW:row_count:exact"
1, # array: "ARROW:null_count:exact"
3, # array: "ARROW:distinct_count:exact"
2, # array: "ARROW:max_value:exact"
0, # array: "ARROW:min_value:exact"
]
types: [ # all values are int64
0,
0,
0,
0,
0,
]
offsets: [
0,
1,
2,
3,
4,
]
复杂数组#
使用嵌套类型。
Schema
struct<a: int32, b: list<item: int64>, c: float64>
数据
[
{a: 1, b: [20, 30, 40], c: 2.9},
{a: 2, b: null, c: -2.9},
{a: 3, b: [99], c: null},
]
统计信息
目标 |
姓名 |
值 |
|---|---|---|
数组 |
行数 |
|
null 的数量 |
|
|
|
null 的数量 |
|
唯一值的数量 |
|
|
近似最大值 |
|
|
近似最小值 |
|
|
|
null 的数量 |
|
|
最大值 |
|
最小值 |
|
|
|
null 的数量 |
|
近似最大值 |
|
|
近似最小值 |
|
列索引
索引 |
目标 |
|---|---|
|
数组 |
|
|
|
|
|
|
|
|
请参阅 RecordBatch 消息 了解如何计算列索引。
统计模式
struct<
column: int32,
statistics: map<
key: dictionary<values: utf8, indices: int32>,
items: dense_union<
# For the number of rows, the number of nulls and so on.
0: int64,
# For the max/min values of c.
1: float64
>
>
>
统计信息数组
column: [
0, # array
1, # a
2, # b
3, # b.item
4, # c
]
statistics:
offsets: [
0,
2, # array: 2 values: [0, 1]
6, # a: 4 values: [2, 3, 4, 5]
7, # b: 1 value: [6]
9, # b.item: 2 values: [7, 8]
12, # c: 3 values: [9, 10, 11]
]
key:
values: [
"ARROW:row_count:exact",
"ARROW:null_count:exact",
"ARROW:distinct_count:exact",
"ARROW:max_value:approximate",
"ARROW:min_value:approximate",
"ARROW:max_value:exact",
"ARROW:min_value:exact",
]
indices: [
0, # "ARROW:row_count:exact"
1, # "ARROW:null_count:exact"
1, # "ARROW:null_count:exact"
2, # "ARROW:distinct_count:exact"
3, # "ARROW:max_value:approximate"
4, # "ARROW:min_value:approximate"
1, # "ARROW:null_count:exact"
5, # "ARROW:max_value:exact"
6, # "ARROW:min_value:exact"
1, # "ARROW:null_count:exact"
3, # "ARROW:max_value:approximate"
4, # "ARROW:min_value:approximate"
]
items:
children:
0: [ # int64
3, # array: "ARROW:row_count:exact"
0, # array: "ARROW:null_count:exact"
0, # a: "ARROW:null_count:exact"
3, # a: "ARROW:distinct_count:exact"
5, # a: "ARROW:max_value:approximate"
0, # a: "ARROW:min_value:approximate"
1, # b: "ARROW:null_count:exact"
99, # b.item: "ARROW:max_value:exact"
20, # b.item: "ARROW:min_value:exact"
1, # c: "ARROW:null_count:exact"
]
1: [ # float64
3.0, # c: "ARROW:max_value:approximate"
-3.0, # c: "ARROW:min_value:approximate"
]
types: [
0, # int64: array: "ARROW:row_count:exact"
0, # int64: array: "ARROW:null_count:exact"
0, # int64: a: "ARROW:null_count:exact"
0, # int64: a: "ARROW:distinct_count:exact"
0, # int64: a: "ARROW:max_value:approximate"
0, # int64: a: "ARROW:min_value:approximate"
0, # int64: b: "ARROW:null_count:exact"
0, # int64: b.item: "ARROW:max_value:exact"
0, # int64: b.item: "ARROW:min_value:exact"
0, # int64: c: "ARROW:null_count:exact"
1, # float64: c: "ARROW:max_value:approximate"
1, # float64: c: "ARROW:min_value:approximate"
]
offsets: [
0, # int64: array: "ARROW:row_count:exact"
1, # int64: array: "ARROW:null_count:exact"
2, # int64: a: "ARROW:null_count:exact"
3, # int64: a: "ARROW:distinct_count:exact"
4, # int64: a: "ARROW:max_value:approximate"
5, # int64: a: "ARROW:min_value:approximate"
6, # int64: b: "ARROW:null_count:exact"
7, # int64: b.item: "ARROW:max_value:exact"
8, # int64: b.item: "ARROW:min_value:exact"
9, # int64: c: "ARROW:null_count:exact"
0, # float64: c: "ARROW:max_value:approximate"
1, # float64: c: "ARROW:min_value:approximate"
]