可变复杂类型

定义表时,可以使用强类型复杂类型来充分描述结构体和数组的任意组合。但是,有时您可能不希望:

  • 如果数据包含具有大量字段的结构体,而在查询中只需要使用其中几个字段,则不必枚举表 DDL 中的剩余部分。此外,如果完全指定复杂类型,则一组深度嵌套的结构体可能会超过表的嵌套限制。

  • 如果数据架构仍在演变,您可以推迟最终确定强类型 DDL。

  • 如果预计在数据中引入新字段,您可以使用可变类型来发现这些字段。另一方面,具有强类型的表会静默忽略这些值。有关使用可变类型发现新字段的示例,请参阅强类型和可变类型

可变类型是一种将复杂或非结构化数据作为二进制 blob 存储在一个列中的方法,它允许访问该数据的各个元素。这与 Vertica 用于 Flex 表的方法相同,Flex 表支持加载非结构化或半结构化数据。在 Flex 表中,源中的所有数据都会加载到一个名为 __raw__ 的 VMap 列中。从此列中,您可以实体化其他列(例如 JSON 数据中的特定字段),或在查询中使用特殊查找函数直接从 __raw__ 列中读取值。

Vertica 对复杂类型会使用类似的方法。您可以使用表定义中的 ROWARRAY 类型来充分描述类型,也可以将复杂类型视为可变类型而不充分描述它。选择以这种方式进行处理的每种复杂类型都将成为其自己的 Flex 样式列。您不必像 Flex 表中那样使用一列包含所有数据;相反,您可以将任何复杂类型的列视为一个类似 Flex 的列,而无论它嵌套的其他类型有多深。

定义可变列

要使用可变复杂类型,请将列声明为 LONG VARBINARY。您可能还需要在解析器中设置其他参数,如解析器文档中所述。

下面我们来探讨包含餐厅表和以下列的 Parquet 文件:

  • name:varchar

  • cuisine 类型:varchar

  • location(城市):数组[varchar]

  • menu:结构体数组,每个结构体都有选项名称和价格

这些数据包含两个复杂的列:location(数组)和 menu(结构体数组)。以下示例将使用 LONG VARBINARY 将这两个列定义为可变列:

=> CREATE EXTERNAL TABLE restaurants(name VARCHAR, cuisine VARCHAR,
        location_city LONG VARBINARY, menu LONG VARBINARY)
    AS COPY FROM '/data/rest*.parquet'
    PARQUET(allow_long_varbinary_match_complex_type='True');

allow_long_varbinary_match_complex_type 参数特定于 Parquet 解析器。如果将任何列定义为可变类型,则该列为必需列。如果不使用此参数,Vertica 会尝试将表中的 LONG VARBINARY 声明与 Parquet 文件中的 VARBINARY 列相匹配,改为查找复杂类型,然后报告数据-类型不匹配。

您不需要将所有复杂列都视为可变类型。以下定义同样有效:

=> CREATE EXTERNAL TABLE restaurants(
            name VARCHAR, cuisine VARCHAR,
            location_city ARRAY[VARCHAR,50],
            menu LONG VARBINARY)
    AS COPY FROM '/data/rest*.parquet'
    PARQUET(allow_long_varbinary_match_complex_type='True');

对于许多常见数据格式,可以使用 INFER_TABLE_DDL 函数从数据文件中派生表定义。此函数几乎在所有情况下都对复杂类型使用强类型。

查询可变列

可变列将存储为 LONG VARBINARY,因此直接选择它们会产生无用的结果。相反,应使用 Flex 映射函数从这些列中提取值。MAPTOSTRING 函数会将复杂类型转换为 JSON,如以下示例所示:

=> SELECT name, location_city, MAPTOSTRING(menu) AS menu FROM restaurants;
       name        |       location_city        |                    menu
-------------------+---------+------------------+----------------------------------------
 Bob's pizzeria    | ["Cambridge","Pittsburgh"] | {
    "0": {
        "item": "cheese pizza",
        "price": "$8.25"
    },
    "1": {
        "item": "spinach pizza",
        "price": "$10.50"
    }
}
 Bakersfield Tacos | ["Pittsburgh"]             | {
    "0": {
        "item": "veggie taco",
        "price": "$9.95"
    },
    "1": {
        "item": "steak taco",
        "price": "$10.95"
    }
}
(2 rows)

menu 列是结构体数组。请注意,输出是一组键/值对,其中键为数组索引。Bob's Pizzeria 的菜单上有两个选项,每个值都是结构体。第一个选项 ("0") 是结构体,"item" 值为 "cheese pizza","price" 为 "$8.25"。

您可以使用键来访问特定值。以下示例将从每个餐厅中选择第一个菜单项。请注意,所有键都是字符串,甚至是数组索引:

=> SELECT name, location_city, menu['0']['item'] AS item, menu['0']['price'] AS price FROM restaurants;
       name        |       location_city        |     item     | price
-------------------+----------------------------+--------------+-------
 Bob's pizzeria    | ["Cambridge","Pittsburgh"] | cheese pizza | $8.25
 Bakersfield Tacos | ["Pittsburgh"]             | veggie taco  | $9.95
(2 rows)

您可以在子查询中使用 MAPITEMS 函数来激增可变类型,而不是访问特定索引,如以下示例所示:

=>  SELECT name, location_city, menu_items['item'], menu_items['price']
    FROM (SELECT mapitems(menu, name, location_city) OVER(PARTITION BEST)
         AS (indexes, menu_items, name, location_city)
    FROM restaurants) explode_menu;
       name        |       location_city        |  menu_items   | menu_items
-------------------+----------------------------+---------------+------------
 Bob's pizzeria    | ["Cambridge","Pittsburgh"] | cheese pizza  | $8.25
 Bob's pizzeria    | ["Cambridge","Pittsburgh"] | spinach pizza | $10.50
 Bakersfield Tacos | ["Pittsburgh"]             | veggie taco   | $9.95
 Bakersfield Tacos | ["Pittsburgh"]             | steak taco    | $10.95
(4 rows)

有关 Flex 映射函数的完整列表,请参阅 Flex 数据函数

JSON 和 avro 可变类型

JSON 和 Avro 的解析器支持复杂类型的可变类型和强类型。当使用可变复杂类型或加载到 Flex 表中时,请使用 flatten_mapsflatten_arrays 参数来控制解析器处理复杂数据的方式。对于强类型的复杂类型,这些解析器会忽略这些参数。

以下示例演示了如何使用可变复杂类型。下面我们来探讨包含以下数据的 JSON 文件:

{
    "name" : "Bob's pizzeria",
    "cuisine" : "Italian",
    "location_city" : ["Cambridge", "Pittsburgh"],
    "menu" : [{"item" : "cheese pizza", "price" : "$8.25"},
              {"item" : "spinach pizza", "price" : "$10.50"}]
}
{
    "name" : "Bakersfield Tacos",
    "cuisine" : "Mexican",
    "location_city" : ["Pittsburgh"],
    "menu" : [{"item" : "veggie taco", "price" : "$9.95"},
              {"item" : "steak taco", "price" : "$10.95"}]
}

创建一个表(使用 LONG VARBINARY 表示可变复杂类型),然后加载指定这些参数的数据:

=> CREATE TABLE restaurant(name VARCHAR, cuisine VARCHAR,
        location_city LONG VARBINARY, menu LONG VARBINARY);

=> COPY restaurant FROM '/data/restaurant.json'
   PARSER FJSONPARSER(flatten_maps=false, flatten_arrays=false);

您可以使用 Flex 函数和直接访问(通过索引)来返回可读值:

=> SELECT MAPTOSTRING(location_city), MAPTOSTRING(menu) FROM restaurant;
                   maptostring                   |             maptostring
-------------------------------------------------+--------------------------------------------------------
 {
    "0": "Cambridge",
    "1": "Pittsburgh"
} | {
    "0": {
        "item": "cheese pizza",
        "price": "$8.25"
    },
    "1": {
        "item": "spinach pizza",
        "price": "$10.50"
    }
}
 {
    "0": "Pittsburgh"
}                       | {
    "0": {
        "item": "veggie taco",
        "price": "$9.95"
    },
    "1": {
        "item": "steak taco",
        "price": "$10.95"
    }
}
(2 rows)

=> SELECT menu['0']['item'] FROM restaurant;
     menu
--------------
 cheese pizza
 veggie taco
(2 rows)

此示例中显示的 COPY 语句将 flatten_maps 设置为 false。如果没有该更改,复杂列的键将无法按预期工作,因为记录和数组键将在顶层“展平”。查询菜单 ['0']['item'] 不会产生任何结果。相反,查询展平值,如下例所示:

=> SELECT menu['0.item'] FROM restaurant;
     menu
--------------
 veggie taco
 cheese pizza
(2 rows)

展平指令适用于整个 COPY 语句。您不能展平某些列而不展平其他列,或者防止展平本身位于展平 Flex 表中的复杂列中的值。因为可变复杂类型和强类型的复杂类型需要不同的值来进行展平,所以不能在同一个加载操作中组合强类型和可变复杂类型。