统计模式#

警告

此规范应被视为实验性的。

原理#

统计信息对于快速查询处理非常有用。 许多查询引擎使用统计信息来优化其查询计划。

Apache Arrow 格式没有统计信息,但其他可以作为 Apache Arrow 数据读取的格式可能有统计信息。 例如,Apache Parquet C++ 实现可以将 Apache Parquet 文件读取为 Apache Arrow 数据,并且 Apache Parquet 文件可能具有统计信息。

我们将统计信息的表示标准化为 Apache Arrow 数组,以便于交换。

用例#

Arrow C 流接口的用例之一如下

  1. 模块 A 将 Apache Parquet 文件读取为 Apache Arrow 数据。

  2. 模块 A 通过 Arrow C 流接口将读取的 Apache Arrow 数据传递给模块 B。

  3. 模块 B 处理传递的 Apache Arrow 数据。

如果模块 A 可以将与 Apache Parquet 文件关联的统计信息传递给模块 B,则模块 B 可以使用统计信息来优化其查询计划。

例如,DuckDB 使用了这种方法,但 DuckDB 无法使用统计信息,因为没有标准化的方法来表示 Apache Arrow 数据的统计信息。

目标#

  • 建立一种将统计信息表示为 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 的详细信息

名称

数据类型

可空

注释

int32

从零开始的列索引,如果统计信息描述的是整个表或记录批次,则为空。

列索引的计算规则与 RecordBatch 消息使用的规则相同。

统计信息

map

目标列、表或记录批次的统计信息。 有关详细信息,请参见下面的单独表格。

这是 statisticsmap 的详细信息

键或项

数据类型

可空

注释

dictionary<values: utf8, indices: int32>

字符串键是统计信息的名称。 字典编码用于提高效率,因为相同的统计信息可能会针对不同的列重复使用。 为精确和近似统计值分配不同的键。 每个统计信息都有其自己的描述,如下所示。

dense_union

统计信息值是密集联合。 它至少具有基于键中的统计信息类型的所有必需类型。 例如,当您具有 int64 的不同计数统计信息和 float64 的平均字节宽度统计信息时,至少需要 int64float64 类型。 请参见下面每个统计信息的描述。

密集联合数组具有每个字段的名称,但我们不标准化这些字段的名称,因为我们可以通过类型代码访问正确的字段。 因此,我们可以为字段使用任何有效的名称。

标准统计信息#

每个统计信息类型都有一个名称,该名称作为每个列或整个表的统计信息映射中的键出现。 dictionary<values: utf8, indices: int32> 用于编码名称以提高空间效率。

我们为同一统计信息的变体分配不同的名称,而不是使用标志。 例如,我们为“不同计数”统计信息的精确值和近似值分配不同的统计信息名称。

冒号符号 : 用作命名空间分隔符,例如 自定义应用程序元数据。 它可以多次在名称中使用。

ARROW 前缀是当前和未来版本的此规范中预定义的统计信息名称的保留命名空间。 用户定义的统计信息不得使用它。 例如,您可以将您的产品名称用作命名空间,例如 MY_PRODUCT:my_statistics:exact

以下是预定义的统计信息名称

名称

数据类型

注释

ARROW:average_byte_width:exact

float64

目标列中一行的平均大小(以字节为单位)。 (精确)

ARROW:average_byte_width:approximate

float64

目标列中一行的平均大小(以字节为单位)。 (近似)

ARROW:distinct_count:exact

int64

目标列中不同值的数量。 (精确)

ARROW:distinct_count:approximate

float64

目标列中不同值的数量。 (近似)

ARROW:max_byte_width:exact

int64

目标列中一行的最大大小(以字节为单位)。 (精确)

ARROW:max_byte_width:approximate

float64

目标列中一行的最大大小(以字节为单位)。 (近似)

ARROW:max_value:exact

目标依赖

目标列中的最大值。 (精确)

ARROW:max_value:approximate

目标依赖

目标列中的最大值。 (近似)

ARROW:min_value:exact

目标依赖

目标列中的最小值。 (精确)

ARROW:min_value:approximate

目标依赖

目标列中的最小值。 (近似)

ARROW:null_count:exact

int64

目标列中的 null 数。 (精确)

ARROW:null_count:approximate

float64

目标列中的 null 数。 (近似)

ARROW:row_count:exact

int64

目标表、记录批次或数组中的行数。 (精确)

ARROW:row_count:approximate

float64

目标表、记录批次或数组中的行数。 (近似)

如果您发现对多个系统可能有用的统计信息,请在 Apache Arrow 开发邮件列表中提出。

当统计信息的生产者和使用者遵循先前约定的统计信息规范时,互操作性会提高。

示例#

以下是一些示例,可帮助您理解。

简单记录批次#

模式

vendor_id: int32
passenger_count: int64

数据

vendor_id:       [5, 1, 5, 1, 5]
passenger_count: [1, 1, 2, 0, null]

统计信息

目标

名称

记录批次

行数

5

vendor_id

null 数

0

不同值的数量

2

最大值

5

最小值

1

passenger_count

null 数

1

不同值的数量

3

最大值

2

最小值

0

列索引

索引

目标

0

vendor_id

1

passenger_count

统计模式

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,
    ]

复杂记录批次#

这使用了嵌套类型。

模式

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"]

统计信息

目标

名称

记录批次

行数

3

col1

null 数

0

col1.a

null 数

0

不同值的数量

3

近似最大值

5

近似最小值

0

col1.b

null 数

1

col1.b.item

最大值

99

最小值

20

col1.c

null 数

1

近似最大值

3.0

近似最小值

-3.0

col2

null 数

1

不同值的数量

2

列索引

索引

目标

0

col1

1

col1.a

2

col1.b

3

col1.b.item

4

col1.c

5

col2

另请参见 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"
    ]

简单数组#

模式

int64

数据

[1, 1, 2, 0, null]

统计信息

目标

名称

数组

行数

5

null 数

1

不同值的数量

3

最大值

2

最小值

0

列索引

索引

目标

0

数组

统计模式

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,
    ]

复杂数组#

这使用了嵌套类型。

模式

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},
]

统计信息

目标

名称

数组

行数

3

null 数

0

a

null 数

0

不同值的数量

3

近似最大值

5

近似最小值

0

b

null 数

1

b.item

最大值

99

最小值

20

c

null 数

1

近似最大值

3.0

近似最小值

-3.0

列索引

索引

目标

0

数组

1

a

2

b

3

b.item

4

c

另请参见 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"
    ]