Snowflake SQL 转换指南
数据类型
数值类型
在 ClickHouse 和 Snowflake 之间迁移数据的用户会立刻注意到,ClickHouse 在数值类型声明方面提供了更细粒度的精度控制。比如,Snowflake 为数值提供了 Number 类型。这要求用户指定精度(数字总位数)和标度(小数点右侧的位数),两者之和最多为 38。整数声明等同于 Number,只是定义了一个固定的精度和标度,且取值范围相同。之所以可以这样做,是因为在 Snowflake 中修改精度(整数的标度为 0)不会影响磁盘上的数据大小——在微分区级别写入时,会为给定数值范围使用所需的最小字节数。不过,标度确实会影响存储空间,但可以通过压缩进行抵消。Float64 类型提供了更宽的取值范围,但会牺牲精度。
与此相对,ClickHouse 为浮点数和整数提供了多种有符号与无符号类型和精度选项。借助这些类型,ClickHouse 用户可以明确指定整数所需的精度,以优化存储和内存开销。Decimal 类型相当于 Snowflake 的 Number 类型,但在 76 位数字上提供了两倍的精度和标度。除了类似的 Float64 类型外,ClickHouse 还提供了 Float32,适用于精度要求较低、压缩优先的场景。
字符串
ClickHouse 和 Snowflake 在字符串数据存储方式上采取了截然不同的策略。Snowflake 中的 VARCHAR 以 UTF-8 存储 Unicode 字符,允许用户指定最大长度。该长度对存储或性能没有影响,始终使用存储字符串所需的最少字节数,只是为下游工具提供有用的约束。其他类型,例如 Text 和 NChar,只是该类型的别名。相反,ClickHouse 使用 String 类型(不需要指定长度)将所有字符串数据以原始字节形式存储,将编码交由用户控制,并提供适用于不同编码的查询时函数。关于背后的动机,我们推荐读者参考 "Opaque data argument"。因此,在实现上,ClickHouse 的 String 更接近于 Snowflake 的 Binary 类型。Snowflake 和 ClickHouse 均支持“排序规则(collation)”,允许用户自定义字符串的排序与比较方式。
半结构化类型
Snowflake 为半结构化数据提供了 VARIANT、OBJECT 和 ARRAY 类型。
ClickHouse 提供了等价的 Variant、Object(现已弃用,推荐使用原生 JSON 类型)以及 Array 类型。此外,ClickHouse 还提供了 JSON 类型,用于取代现已弃用的 Object('json') 类型,并且在与其他原生 JSON 类型的对比中表现出特别优越的性能和存储效率。
ClickHouse 还支持具名的 Tuple 以及通过 Nested 类型实现的 Tuple 数组,使用户可以显式映射嵌套结构。这样可以在整个层级结构中应用编解码器和类型优化;而 Snowflake 则要求用户在外层对象上使用 OBJECT、VARIANT 和 ARRAY 类型,并且不允许显式指定内部类型。这种内部类型定义也简化了在 ClickHouse 中对嵌套数值的查询,这些数值无需强制类型转换即可用于索引定义。
在 ClickHouse 中,编解码器和优化类型同样可以应用到子结构上。这带来的额外好处是,嵌套结构下的压缩效果依然非常出色,并且与扁平化数据相当。相比之下,由于无法对子结构应用特定类型,Snowflake 建议通过扁平化数据以获得最佳压缩效果。Snowflake 还对这些数据类型施加了大小限制。
类型参考
| Snowflake | ClickHouse | 注意 | |
|---|---|---|---|
NUMBER | Decimal | ClickHouse 所支持的精度和小数位数是 Snowflake 的两倍——可达 76 位数字,而 Snowflake 为 38 位。 | |
FLOAT, FLOAT4, FLOAT8 | Float32, Float64 | Snowflake 中的所有浮点数都是 64 位。 | |
VARCHAR | String | ||
BINARY | String | ||
BOOLEAN | Bool | ||
DATE | Date, Date32 | Snowflake 中的 DATE 支持比 ClickHouse 更宽的日期范围,例如,Date32 的最小值是 1900-01-01,而 Date 的最小值是 1970-01-01。ClickHouse 中的 Date 使用更节省成本的(两个字节)存储。 | |
TIME(N) | 没有完全对应的类型,但可以用 DateTime 和 DateTime64(N) 来表示。 | DateTime64 遵循相同的精度概念。 | |
TIMESTAMP - TIMESTAMP_LTZ, TIMESTAMP_NTZ, TIMESTAMP_TZ | DateTime 和 DateTime64 | DateTime 和 DateTime64 列可以可选地定义一个 TZ 参数。若未指定,则使用服务器的时区。此外,客户端还可以使用 --use_client_time_zone 参数。 | |
VARIANT | JSON, Tuple, Nested | ClickHouse 中的 JSON 类型目前是实验性特性。该类型会在插入数据时推断列的类型。也可以使用 Tuple、Nested 和 Array 来显式构建具有明确类型的结构,作为替代方案。 | |
OBJECT | Tuple, Map, JSON | OBJECT 和 Map 都类似于 ClickHouse 中的 JSON 类型,其中键为 String。ClickHouse 要求值在类型上保持一致并且为强类型,而 Snowflake 使用 VARIANT,这意味着不同键的值可以是不同的类型。如果在 ClickHouse 中需要这种行为,请使用 Tuple 显式定义层级结构,或使用 JSON 类型。 | |
ARRAY | Array, Nested | Snowflake 中的 ARRAY 对其元素使用 VARIANT——一种超类型。而在 ClickHouse 中,这些元素是强类型的。 | |
GEOGRAPHY | Point, Ring, Polygon, MultiPolygon | Snowflake 强制使用坐标系(WGS 84),而 ClickHouse 则在查询时才应用坐标系。 | |
GEOMETRY | Point, Ring, Polygon, MultiPolygon |
| ClickHouse Type | Description |
|---|---|
IPv4 and IPv6 | 面向 IP 的专用数据类型,相比 Snowflake 可能支持更高效的存储。 |
FixedString | 允许使用固定字节长度,对存储哈希值非常有用。 |
LowCardinality | 允许对任意类型进行字典编码,适用于基数预期小于 100k 的场景。 |
Enum | 允许在 8 或 16 位范围内对具名值进行高效编码。 |
UUID | 用于高效存储 UUID。 |
Array(Float32) | 向量可以表示为由 Float32 组成的 Array,并支持距离函数。 |
最后,ClickHouse 提供了一个独特的能力,即存储聚合函数的中间 状态。这一状态依赖具体实现, 但它允许将聚合结果存储起来,并在之后通过相应的合并函数进行查询。通常,这一 特性是通过物化视图来使用的,并且如下面所示,可以在仅增加极少量存储成本的情况 下,通过存储针对已插入数据的查询增量结果(更多细节见后文)来提升特定查询的性能。