半结构化数据类型¶
以下 Snowflake 数据类型可以包含其他数据类型:
VARIANT(可以包含任何其他数据类型的值)。
OBJECT(可以直接包含 VARIANT 值,从而间接包含任何其他数据类型的值,包括其自身)。
ARRAY(可以直接包含 VARIANT 值,从而间接包含任何其他数据类型的值,包括其自身)。
我们通常将这些数据类型称为 半结构化 数据类型。严格来说, OBJECT 是这些数据类型中唯一一种本身具有真正 半结构化数据类型 <https://en.wikipedia.org/wiki/Semi-structured_data>`_所有特征的数据类型。但是,组合这些数据类型可以显式表示任意 :ref:`分层数据结构 <label-hierarchical_data>
,这些结构可用于加载和操作半结构化格式(例如 JSON、Avro、ORC、Parquet 或 XML)的数据。
本主题介绍了其中每种数据类型。
VARIANT¶
VARIANT 值可以存储任何其他类型的值,包括 OBJECT 和 ARRAY 值。
VARIANT 值的特征¶
VARIANT 值的最大未压缩数据可达到 16 MB。但实际上,由于内部开销,最大大小通常较小。最大大小还取决于要存储的对象。
插入 VARIANT 数据¶
要直接插入 VARIANT 数据,请使用 INSERT INTO ... SELECT
。下面的示例展示了如何将 JSON 格式的数据插入到 VARIANT 值中:
CREATE OR REPLACE TABLE variant_insert (v VARIANT);
INSERT INTO variant_insert (v)
SELECT PARSE_JSON('{"key3": "value3", "key4": "value4"}');
SELECT * FROM variant_insert;
+---------------------+
| V |
|---------------------|
| { |
| "key3": "value3", |
| "key4": "value4" |
| } |
+---------------------+
使用 VARIANT 值¶
要将值转换为 VARIANT 数据类型,或从该数据类型转换为其他类型,可以使用 CAST 函数、TO_VARIANT 函数或 ::
运算符(例如 expression::VARIANT
)进行显式转换。
在某些情况下,可以将值隐式转换为 VARIANT 值。有关详细信息,请参阅 数据类型转换。
下面的示例代码演示了如何使用 VARIANT 值,包括如何从 VARIANT 值转换以及如何转换为 VARIANT 值。
创建一个表并插入一个值:
CREATE OR REPLACE TABLE varia (float1 FLOAT, v VARIANT, float2 FLOAT);
INSERT INTO varia (float1, v, float2) VALUES (1.23, NULL, NULL);
第一个 UPDATE 将值从 FLOAT 值转换为 VARIANT 值。第二个 UPDATE 将 VARIANT 值转换为 FLOAT 值。
UPDATE varia SET v = TO_VARIANT(float1); -- converts from a FLOAT value to a VARIANT value.
UPDATE varia SET float2 = v::FLOAT; -- converts from a VARIANT value to a FLOAT value.
SELECT 所有值:
SELECT * FROM varia;
+--------+-----------------------+--------+
| FLOAT1 | V | FLOAT2 |
|--------+-----------------------+--------|
| 1.23 | 1.230000000000000e+00 | 1.23 |
+--------+-----------------------+--------+
如上例所示,要转换 VARIANT 数据类型的值,请将 VARIANT 值转换为目标数据类型。例如,下面的语句使用 ::
运算符将 VARIANT 转换为 FLOAT:
SELECT my_variant_column::FLOAT * 3.14 FROM ...;
VARIANT 数据存储值和值的数据类型。因此,您可以在表达式中使用 VARIANT 值,其中值的数据类型有效,而无需先转换 VARIANT。例如,如果 VARIANT 列 my_variant_column
包含一个数值,则可以直接将 my_variant_column
乘以另一个数值:
SELECT my_variant_column * 3.14 FROM ...;
可以使用 TYPEOF 函数检索值的本机数据类型。
默认情况下,从 VARIANT 列检索 VARCHAR、DATE、TIME 和 TIMESTAMP 值时,这些值都会放在双引号内。您可以通过将值显式转换为基础数据类型(例如,从 VARIANT 转换为 VARCHAR)来消除双引号。例如:
SELECT 'Sample', 'Sample'::VARIANT, 'Sample'::VARIANT::VARCHAR;
+----------+-------------------+----------------------------+
| 'SAMPLE' | 'SAMPLE'::VARIANT | 'SAMPLE'::VARIANT::VARCHAR |
|----------+-------------------+----------------------------|
| Sample | "Sample" | Sample |
+----------+-------------------+----------------------------+
VARIANT 值可以缺失(包含 SQL NULL),这与 VARIANT null 值不同,后者是用于表示半结构化数据中的 null 值的实际值。VARIANT null 是一个与自身相等的真值。有关更多信息,请参阅 VARIANT null。
如果数据是从 JSON 格式加载并存储在 VARIANT 列中,则以下注意事项适用:
对于大多数常规且仅使用原生 JSON 类型(例如字符串和数字)的数据,对关系数据和 VARIANT 列中数据的存储和查询操作的性能非常相似。
对于非原生数据(例如日期和时间戳),在加载到 VARIANT 列时,这些值会以字符串的形式存储。因此,相较于存储在具有相应数据类型的关系列中,对这些值的操作可能较慢,并且还会使用更多的空间。
有关使用 VARIANT 数据类型的更多信息,请参阅 存储在 VARIANT 中的半结构化数据的注意事项。
有关查询 VARIANT 列中存储的半结构化数据的更多信息,请参阅 查询半结构化数据。
VARIANT 数据的常见用途¶
VARIANT 数据通常用于以下情况:
OBJECT¶
Snowflake OBJECT 值类似于 `JSON“object”<http://json.org>`__。在其他编程语言中,相应的数据类型通常称为“dictionary”、“hash”或“map”。
OBJECT 值包含键值对。
OBJECT 值的特征¶
在 Snowflake 半结构化 OBJECT 数据中,每个键都是 VARCHAR,每个值都是 VARIANT 值。
由于 VARIANT 值可以存储任何其他数据类型的值,因此不同的 VARIANT 值(在不同的键值对中)可以具有不同的基础数据类型。例如,OBJECT 值可以以 VARCHAR 值保存人名,以 INTEGER 值保存人的年龄。在下面的示例中,姓名和年龄都转换为 VARIANT 值。
SELECT OBJECT_CONSTRUCT(
'name', 'Jones'::VARIANT,
'age', 42::VARIANT);
以下注意事项适用于 OBJECT 数据:
Snowflake 当前不支持显式类型对象。
在键值对中,键不应为空字符串,键和值都不应为 NULL。
OBJECT 值的最大长度为 16 MB。
OBJECT 值可以包含 半结构化数据 (link removed)。
OBJECT 值可用于创建 分层数据结构。
备注
Snowflake 还支持结构化 OBJECT 数据类型,允许 VARIANT 值以外的值。结构化 OBJECT 类型还定义了该类型的 OBJECT 值中必须存在的键。有关更多信息,请参阅 结构化数据类型。
插入 OBJECT 数据¶
要直接插入 OBJECT 数据,请使用 INSERT INTO ... SELECT
。
以下示例使用 OBJECT_CONSTRUCT 函数构造它插入的 OBJECT 值。
CREATE OR REPLACE TABLE object_example (object_column OBJECT);
INSERT INTO object_example (object_column)
SELECT OBJECT_CONSTRUCT('thirteen', 13::VARIANT, 'zero', 0::VARIANT);
SELECT * FROM object_example;
+-------------------+
| OBJECT_COLUMN |
|-------------------|
| { |
| "thirteen": 13, |
| "zero": 0 |
| } |
+-------------------+
在每个键值对中,值都显式转换为 VARIANT。在这些情况下,不需要显式类型转换。Snowflake 可以隐式转换为 VARIANT。(有关隐式类型转换的信息,请参阅 数据类型转换。)
您还可以使用 OBJECT 常量指定要插入的 OBJECT 值。有关更多信息,请参阅 OBJECT 常量。
OBJECT 常量¶
常量*(也称为 *字面量)是指固定的数据值。Snowflake 支持使用常量来指定 OBJECT 值。OBJECT 常量用大括号({
和 }
)分隔。
OBJECT 常量有以下语法:
{ [<key>: <value> [, <key>: <value> , ...]] }
其中:
key
键值对中的键。
key
必须是字符串字面量。value
与键关联的值。
value
可以是字面量或表达式。value
可以是任何数据类型。
以下是指定 OBJECT 常量的示例:
{}
为空 OBJECT 值。{ 'key1': 'value1' , 'key2': 'value2' }
包含 OBJECT 值的指定键值对,使用值的字面量。{ 'key1': c1+1 , 'key2': c1+2 }
包含 OBJECT 值的指定键值对,使用值的表达式。
{*}
通配符使用属性名称作为键,使用关联值作为值,从而基于指定数据构造 OBJECT 值。在对象常量中指定时,通配符可以无限定,也可以使用表名或别名限定。例如,这两个通配符规范都有效:
SELECT {*} FROM my_table; SELECT {my_table1.*} FROM my_table1 INNER JOIN my_table2 ON my_table2.col1 = my_table1.col1;
您可以在对象常量中使用 ILIKE 和 EXCLUDE 关键字。要选择特定列,请使用 ILIKE 关键字。例如,以下查询选择与表
my_table
中的模式col1%
匹配的列:SELECT {* ILIKE 'col1%'} FROM my_table;
要排除特定列,请使用 EXCLUDE 关键字。例如,以下查询排除表
my_table
中的col1
:SELECT {* EXCLUDE col1} FROM my_table;
以下查询排除表
my_table
中的col1
和col2
:SELECT {* EXCLUDE (col1, col2)} FROM my_table;
通配符不能与键值对混合。例如,不允许使用以下通配符规范:
SELECT {*, 'k': 'v'} FROM my_table;
一个对象常量中不能使用多个通配符。例如,不允许使用以下通配符规范:
SELECT {t1.*, t2.*} FROM t1, t2;
以下语句使用 OBJECT 常量和 OBJECT_CONSTRUCT 函数,将 OBJECT 数据插入表中。OBJECT 值包含加拿大两个省的名称和省会城市。
CREATE OR REPLACE TABLE my_object_table (my_object OBJECT);
INSERT INTO my_object_table (my_object)
SELECT { 'PROVINCE': 'Alberta'::VARIANT , 'CAPITAL': 'Edmonton'::VARIANT };
INSERT INTO my_object_table (my_object)
SELECT OBJECT_CONSTRUCT('PROVINCE', 'Manitoba'::VARIANT , 'CAPITAL', 'Winnipeg'::VARIANT );
SELECT * FROM my_object_table;
+--------------------------+
| MY_OBJECT |
|--------------------------|
| { |
| "CAPITAL": "Edmonton", |
| "PROVINCE": "Alberta" |
| } |
| { |
| "CAPITAL": "Winnipeg", |
| "PROVINCE": "Manitoba" |
| } |
+--------------------------+
以下示例从 FROM 子句获取属性名称和值,使用通配符 ({*}
) 插入 OBJECT 数据。首先,创建一个名为 demo_ca_provinces
的表,该表的 VARCHAR 值包含省和省会名称:
CREATE OR REPLACE TABLE demo_ca_provinces (province VARCHAR, capital VARCHAR);
INSERT INTO demo_ca_provinces (province, capital) VALUES
('Ontario', 'Toronto'),
('British Columbia', 'Victoria');
SELECT province, capital
FROM demo_ca_provinces
ORDER BY province;
+------------------+----------+
| PROVINCE | CAPITAL |
|------------------+----------|
| British Columbia | Victoria |
| Ontario | Toronto |
+------------------+----------+
使用 demo_ca_provinces
表中的数据将对象数据插入 my_object_table
:
INSERT INTO my_object_table (my_object)
SELECT {*} FROM demo_ca_provinces;
SELECT * FROM my_object_table;
+----------------------------------+
| MY_OBJECT |
|----------------------------------|
| { |
| "CAPITAL": "Edmonton", |
| "PROVINCE": "Alberta" |
| } |
| { |
| "CAPITAL": "Winnipeg", |
| "PROVINCE": "Manitoba" |
| } |
| { |
| "CAPITAL": "Toronto", |
| "PROVINCE": "Ontario" |
| } |
| { |
| "CAPITAL": "Victoria", |
| "PROVINCE": "British Columbia" |
| } |
+----------------------------------+
以下示例使用表达式表示 OBJECT 常量中的值:
SET my_variable = 10;
SELECT {'key1': $my_variable+1, 'key2': $my_variable+2};
+--------------------------------------------------+
| {'KEY1': $MY_VARIABLE+1, 'KEY2': $MY_VARIABLE+2} |
|--------------------------------------------------|
| { |
| "key1": 11, |
| "key2": 12 |
| } |
+--------------------------------------------------+
SQL 语句使用单引号指定 OBJECT 值内的字符串字面量(如 Snowflake SQL 中的其他位置),但 OBJECT 值内的字符串字面量带双引号显示:
SELECT { 'Manitoba': 'Winnipeg' } AS province_capital;
+--------------------------+
| PROVINCE_CAPITAL |
|--------------------------|
| { |
| "Manitoba": "Winnipeg" |
| } |
+--------------------------+
通过键访问 OBJECT 值的元素¶
要检索 OBJECT 值中的值,请在 方括号 中指定键,如下所示:
SELECT my_variant_column['key1'] FROM my_table;
您也可以使用冒号运算符。以下命令显示,无论使用方括号还是冒号,结果都是相同的:
SELECT object_column['thirteen'],
object_column:thirteen
FROM object_example;
+---------------------------+------------------------+
| OBJECT_COLUMN['THIRTEEN'] | OBJECT_COLUMN:THIRTEEN |
|---------------------------+------------------------|
| 13 | 13 |
+---------------------------+------------------------+
有关冒号运算符的更多信息,请参阅 点表示法,其中介绍了如何使用 :
和 .
运算符访问嵌套数据。
OBJECT 数据的常见用途¶
当以下一项或多项为 True 时,通常使用 OBJECT 数据:
您有多条数据由字符串标识。例如,如果要按省份名称查找信息,您可能需要使用 OBJECT 值。
您希望将有关数据的信息与数据一起存储;名称(键)不仅是不同的标识符,而且是有意义的。
信息没有自然顺序,或者只能从键中推断出顺序。
数据的结构各不相同,或者数据可能不完整。例如,如果要创建通常包含书名、作者姓名和出版日期的图书目录,但在某些情况下出版日期未知,则可能需要使用 OBJECT 值。
ARRAY¶
Snowflake 数组类似于许多其他编程语言中的数组。数组包含 0 条或多条数据。通过指定每个元素在数组中的位置来访问每个元素。
数组的特征¶
半结构化数组中的每个值都是 VARIANT 类型。VARIANT 值可以包含任何其他数据类型的值。
其他数据类型的值可以转换为 VARIANT 值,然后存储在数组中。数组的某些函数(包括 ARRAY_CONSTRUCT)可以为您将值 隐式地转换 为 VARIANT 值。
由于数组存储 VARIANT 值,并且由于 VARIANT 值可以在其中存储其他数据类型,因此数组中值的基础数据类型不必完全相同。但是,在大多数情况下,数据元素属于相同或兼容的类型,因此可以以相同的方式处理所有数据元素。
以下注意事项适用于数组:
Snowflake 不支持特定非 VARIANT 类型的元素数组。
声明 Snowflake 数组时不指定元素数。数组可以根据 ARRAY_APPEND 等操作动态增长。Snowflake 目前不支持固定大小的数组。
数组可以包含 NULL 值。
数组中所有值的理论最大组合大小为 16 MB。但是,数组具有内部开销。实际的最大数据大小通常较小,具体取决于元素的数量和值。
备注
Snowflake 还支持结构化数组,它允许 VARIANT 以外类型的元素。有关更多信息,请参阅 结构化数据类型。
插入 ARRAY 数据¶
要直接插入 ARRAY 数据,请使用 INSERT INTO ... SELECT
。
以下代码使用 ARRAY_CONSTRUCT 函数构造它插入的数组。
CREATE OR REPLACE TABLE array_example (array_column ARRAY);
INSERT INTO array_example (array_column)
SELECT ARRAY_CONSTRUCT(12, 'twelve', NULL);
您还可以使用 ARRAY 常量指定要插入的数组。有关更多信息,请参阅 ARRAY 常量。
ARRAY 常量¶
常量*(也称为 *字面量)是指固定的数据值。Snowflake 支持使用常量来指定 ARRAY 值。ARRAY 常量用方括号([
和 ]
)分隔。
ARRAY 常量有以下语法:
[<value> [, <value> , ...]]
其中:
value
与数组元素关联的值。
value
可以是字面量或表达式。value
可以是任何数据类型。
以下是指定 ARRAY 常量的示例:
[]
为空 ARRAY 值。[ 1 , 'value1' ]
包含 ARRAY 常量的指定值,使用值的字面量。[ c1+1 , c1+2 ]
包含 ARRAY 常量的指定值,使用值的表达式。
以下示例使用 ARRAY 常量来指定要插入的数组。
INSERT INTO array_example (array_column)
SELECT [ 12, 'twelve', NULL ];
以下语句使用 ARRAY 常量和 ARRAY_CONSTRUCT 函数来执行相同的任务:
UPDATE my_table SET my_array = [ 1, 2 ];
UPDATE my_table SET my_array = ARRAY_CONSTRUCT(1, 2);
以下示例使用表达式表示 ARRAY 常量中的值:
SET my_variable = 10;
SELECT [$my_variable+1, $my_variable+2];
+----------------------------------+
| [$MY_VARIABLE+1, $MY_VARIABLE+2] |
|----------------------------------|
| [ |
| 11, |
| 12 |
| ] |
+----------------------------------+
SQL 语句使用单引号指定数组内的字符串字面量(如 Snowflake SQL 中的其他位置),但数组内的字符串字面量带双引号显示:
SELECT [ 'Alberta', 'Manitoba' ] AS province;
+--------------+
| PROVINCE |
|--------------|
| [ |
| "Alberta", |
| "Manitoba" |
| ] |
+--------------+
通过索引或切片访问数组的元素¶
数组索引从 0 开始,因此数组中的第一个元素是元素 0。
通过在方括号中指定数组元素的索引号来访问数组中的值。例如,以下查询读取 my_array_column
中存储的数组中位于索引位置 2
的值。
SELECT my_array_column[2] FROM my_table;
数组可以嵌套。以下查询读取嵌套数组的第零个元素的第零个元素:
SELECT my_array_column[0][0] FROM my_table;
尝试访问数组末尾之外的元素将返回 NULL。
数组的 切片 是相邻元素的序列(即数组的连续子集)。
您可以通过调用 ARRAY_SLICE 函数来访问数组的切片。例如:
SELECT ARRAY_SLICE(my_array_column, 5, 10) FROM my_table;
ARRAY_SLICE 函数返回从指定的起始元素(上例中的 5)到指定的结束元素(上例中的 10,但不包括 该结束元素)之间的元素。
空数组或空切片通常由一对方括号(中间没有任何内容)表示 ([]
)。
密集和稀疏阵列¶
数组可以是 密集 数组或 稀疏 数组。
在密集数组中,元素的索引值从零开始,并且是连续的(0、1、2 等)。但是,在稀疏数组中,索引值可以是非连续的(例如 0、2、5)。值不需要从 0 开始。
如果索引没有相应的元素,则与该索引对应的值称为 undefined。例如,如果稀疏数组有三个元素,并且这些元素位于索引 0、2 和 5 处,则索引 1、3 和 4 处的元素为 undefined
。
0 2 5
+-----+.....+-------+.....+.....+------+
| Ann | | Carol | | | Fred |
+-----+.....+-------+.....+.....+------+
^ ^ ^
| | |
undefined--------------
undefined 元素被视为元素。例如,注意前面的稀疏数组示例,该示例包含索引 0、2 和 5 处的元素(索引 5 之后没有任何元素)。如果读取包含索引 3 和 4 处元素的切片,则输出类似于以下内容:
[ undefined, undefined ]
尝试访问数组末尾之外的切片会导致得到空数组,而不是 undefined
值的数组。以下 SELECT 语句尝试读取示例稀疏数组中最后一个元素之外的内容:
SELECT ARRAY_SLICE(array_column, 6, 8) FROM table_1;
输出是一个空数组:
+---------------------------------+
| array_slice(array_column, 6, 8) |
+---------------------------------+
| [ ] |
+---------------------------------+
请注意,undefined
与 NULL 不同。数组中的 NULL 值是定义的元素。
在密集数组中,每个元素都会占用存储空间,即使元素的值为 NULL。
在稀疏数组中,undefined
元素不直接占用存储空间。
在密集数组中,索引值的理论范围是从 0 到 16777215。(最大理论元素数为 16777216,因为大小上限为 16 MB [16777216 个字节],最小可能值为 1 个字节。)
在稀疏数组中,索引值的理论范围是从 0 到 231 - 1。但是,由于 16 MB 的限制,稀疏数组不能容纳 231 值。最大理论值数仍限制为 16777216。
(请记住,由于内部开销,密集数组和稀疏数组的实际大小限制至少略小于理论最大值 16 MB。)
您可以使用 ARRAY_INSERT 函数在数组中的特定索引点插入值(保持其他数组元素 undefined
)来创建稀疏数组。请注意,由于 ARRAY_INSERT 向右推送元素会更改访问它们所需的索引值,因此您几乎总是要从左到右填充稀疏数组(即从 0 向上,为插入的每个新值增加索引值)。
ARRAY 数据的常见用途¶
当以下一项或多项为 True 时,通常使用 ARRAY 数据:
存在一个数据集合,集合中每个片段的结构相同或相似。
每条数据都应以类似的方式处理。例如,您可以循环遍历数据,以相同的方式处理每条数据。
数据具有自然顺序,例如按时间顺序排列。
示例¶
以下示例显示了包含 VARIANT、ARRAY 和 OBJECT 数据的表上 DESC TABLE 命令的输出。
CREATE OR REPLACE TABLE test_semi_structured(
var VARIANT,
arr ARRAY,
obj OBJECT);
DESC TABLE test_semi_structured;
+------+---------+--------+-------+---------+-------------+------------+-------+------------+---------+-------------+----------------+
| name | type | kind | null? | default | primary key | unique key | check | expression | comment | policy name | privacy domain |
|------+---------+--------+-------+---------+-------------+------------+-------+------------+---------+-------------+----------------|
| VAR | VARIANT | COLUMN | Y | NULL | N | N | NULL | NULL | NULL | NULL | NULL |
| ARR | ARRAY | COLUMN | Y | NULL | N | N | NULL | NULL | NULL | NULL | NULL |
| OBJ | OBJECT | COLUMN | Y | NULL | N | N | NULL | NULL | NULL | NULL | NULL |
+------+---------+--------+-------+---------+-------------+------------+-------+------------+---------+-------------+----------------+
此示例演示了如何在表中加载简单值,以及查询表时这些值的外观。
创建表并加载数据:
CREATE TABLE demonstration1 (
ID INTEGER,
array1 ARRAY,
variant1 VARIANT,
object1 OBJECT);
INSERT INTO demonstration1 (id, array1, variant1, object1)
SELECT
1,
ARRAY_CONSTRUCT(1, 2, 3),
PARSE_JSON(' { "key1": "value1", "key2": "value2" } '),
PARSE_JSON(' { "outer_key1": { "inner_key1A": "1a", "inner_key1B": "1b" }, '
||
' "outer_key2": { "inner_key2": 2 } } ');
INSERT INTO demonstration1 (id, array1, variant1, object1)
SELECT
2,
ARRAY_CONSTRUCT(1, 2, 3, NULL),
PARSE_JSON(' { "key1": "value1", "key2": NULL } '),
PARSE_JSON(' { "outer_key1": { "inner_key1A": "1a", "inner_key1B": NULL }, '
||
' "outer_key2": { "inner_key2": 2 } '
||
' } ');
现在显示表中的数据。
SELECT *
FROM demonstration1
ORDER BY id;
+----+-------------+---------------------+--------------------------+
| ID | ARRAY1 | VARIANT1 | OBJECT1 |
|----+-------------+---------------------+--------------------------|
| 1 | [ | { | { |
| | 1, | "key1": "value1", | "outer_key1": { |
| | 2, | "key2": "value2" | "inner_key1A": "1a", |
| | 3 | } | "inner_key1B": "1b" |
| | ] | | }, |
| | | | "outer_key2": { |
| | | | "inner_key2": 2 |
| | | | } |
| | | | } |
| 2 | [ | { | { |
| | 1, | "key1": "value1", | "outer_key1": { |
| | 2, | "key2": null | "inner_key1A": "1a", |
| | 3, | } | "inner_key1B": null |
| | undefined | | }, |
| | ] | | "outer_key2": { |
| | | | "inner_key2": 2 |
| | | | } |
| | | | } |
+----+-------------+---------------------+--------------------------+
有关其他示例,请参阅 查询半结构化数据。