⌘+k ctrl+k
1.1.3 (stable)
Search Shortcut cmd + k | ctrl + k
Struct Data Type

从概念上讲,STRUCT 列包含一个称为“条目”的有序列。这些条目通过字符串名称引用。本文档将这些条目名称称为键。STRUCT 列中的每一行必须具有相同的键。结构条目的名称是模式的一部分。STRUCT 列中的每一行必须具有相同的布局。结构条目的名称不区分大小写。

STRUCT通常用于将多个列嵌套到一个列中,嵌套的列可以是任何类型,包括其他STRUCTLIST

STRUCTs 类似于 PostgreSQL 的 ROW 类型。关键区别在于 DuckDB 的 STRUCTs 要求在 STRUCT 列的每一行中具有相同的键。这使得 DuckDB 能够通过充分利用其向量化执行引擎显著提高性能,并且还强制类型一致性以提高正确性。DuckDB 包含一个 row 函数作为生成 STRUCT 的特殊方式,但没有 ROW 数据类型。请参阅下面的示例和 STRUCT 函数文档 以获取详细信息。

STRUCT具有固定的模式。无法使用UPDATE操作更改STRUCT的模式。

See the data types overview for a comparison between nested data types.

创建结构体

可以使用struct_pack(name := expr, ...)函数、等效的数组表示法{'name': expr, ...}、使用行变量或使用row函数来创建结构体。

使用struct_pack函数创建一个结构体。注意键周围没有单引号,并且使用了:=操作符:

SELECT struct_pack(key1 := 'value1', key2 := 42) AS s;

使用数组表示法创建一个结构体:

SELECT {'key1': 'value1', 'key2': 42} AS s;

使用行变量创建一个结构体:

SELECT d AS s FROM (SELECT 'value1' AS key1, 42 AS key2) d;

创建一个整数结构体:

SELECT {'x': 1, 'y': 2, 'z': 3} AS s;

创建一个带有NULL值的字符串结构体:

SELECT {'yes': 'duck', 'maybe': 'goose', 'huh': NULL, 'no': 'heron'} AS s;

为每个键创建一个具有不同类型的结构体:

SELECT {'key1': 'string', 'key2': 1, 'key3': 12.345} AS s;

创建一个包含NULL值的结构体:

SELECT {
        'birds': {'yes': 'duck', 'maybe': 'goose', 'huh': NULL, 'no': 'heron'},
        'aliens': NULL,
        'amphibians': {'yes': 'frog', 'maybe': 'salamander', 'huh': 'dragon', 'no': 'toad'}
    } AS s;

向结构体添加字段/值

添加到整数结构体:

SELECT struct_insert({'a': 1, 'b': 2, 'c': 3}, d := 4) AS s;

从结构体中检索

从结构体中检索值可以使用点符号、括号符号或通过结构体函数struct_extract来实现。

使用点符号来检索键位置的值。在以下查询中,子查询生成一个结构列 a,然后我们使用 a.x 进行查询。

SELECT a.x FROM (SELECT {'x': 1, 'y': 2, 'z': 3} AS a);

如果键包含空格,只需将其用双引号括起来(")。

SELECT a."x space" FROM (SELECT {'x space': 1, 'y': 2, 'z': 3} AS a);

也可以使用括号表示法。请注意,这里使用单引号('),因为目的是指定特定的字符串键,并且括号内只能使用常量表达式(不能使用表达式):

SELECT a['x space'] FROM (SELECT {'x space': 1, 'y': 2, 'z': 3} AS a);

struct_extract 函数也是等效的。这将返回1:

SELECT struct_extract({'x space': 1, 'y': 2, 'z': 3}, 'x space');

STRUCT.*

与从结构体中检索单个键不同,星号表示法(*)可用于从结构体中检索所有键作为单独的列。 这在先前的操作创建了未知形状的结构体时特别有用,或者如果查询必须处理任何潜在的结构体键时。

结构体中的所有键可以使用 * 作为单独的列返回:

SELECT a.*
FROM (SELECT {'x': 1, 'y': 2, 'z': 3} AS a);
x y z
1 2 3

点符号操作顺序

使用点符号引用结构体可能会与引用模式和表产生歧义。通常,DuckDB首先查找列,然后在列中查找结构体键。DuckDB按照以下顺序解析引用,使用第一个匹配项:

无点

SELECT part1
FROM tbl;
  1. part1 是一个列

One Dot

SELECT part1.part2
FROM tbl;
  1. part1 是一个表,part2 是一个列
  2. part1 是一个列,part2 是该列的一个属性

两个(或更多)点

SELECT part1.part2.part3
FROM tbl;
  1. part1 是一个模式,part2 是一个表,part3 是一个列
  2. part1 是一个表,part2 是一个列,part3 是该列的一个属性
  3. part1 是一个列,part2 是该列的一个属性,part3 是该列的另一个属性

任何额外的部分(例如,.part4.part5,等等)总是被视为属性

使用row函数创建结构体

row 函数可用于自动将多列转换为单个结构体列。 使用 row 时,键将为空字符串,以便轻松插入到具有结构体列的表中。 然而,列不能使用 row 函数初始化,必须显式命名。 例如,使用 row 函数将值插入到结构体列中:

CREATE TABLE t1 (s STRUCT(v VARCHAR, i INTEGER));
INSERT INTO t1 VALUES (row('a', 42));
SELECT * FROM t1;

表格将包含一个条目:

{'v': a, 'i': 42}

以下产生与上述相同的结果:

CREATE TABLE t1 AS (
    SELECT row('a', 42)::STRUCT(v VARCHAR, i INTEGER)
);

使用row函数初始化结构体列将会失败:

CREATE TABLE t2 AS SELECT row('a');
Invalid Input Error: A table cannot be created from an unnamed struct

在转换结构体时,字段名称必须匹配。因此,以下查询将失败:

SELECT a::STRUCT(y INTEGER) AS b
FROM
    (SELECT {'x': 42} AS a);
Mismatch Type Error: Type STRUCT(x INTEGER) does not match with STRUCT(y INTEGER). Cannot cast STRUCTs - element "x" in source struct was not found in target struct

解决这个问题的方法是使用 struct_pack 代替:

SELECT struct_pack(y := a.x) AS b
FROM
    (SELECT {'x': 42} AS a);

row 函数可用于返回未命名的结构体。例如:

SELECT row(x, x + 1, y) FROM (SELECT 1 AS x, 'a' AS y) AS s;

这将产生 (1, 2, a)

如果在创建结构体时使用多个表达式,row 函数是可选的。以下查询返回的结果与之前的结果相同:

SELECT (x, x + 1, y) AS s FROM (SELECT 1 AS x, 'a' AS y);

Comparison and Ordering

STRUCT 类型可以使用所有的比较运算符进行比较。 这些比较可以用于逻辑表达式中, 例如 WHEREHAVING 子句,并返回BOOLEAN

对于比较,STRUCT的键具有固定的位置顺序,从左到右。 比较行为与行比较相同,因此,匹配的键必须位于相同的位置。

具体来说,对于任何STRUCT比较,适用以下规则:

  • 相等性。 s1s2 是相等的,如果所有对应的值都相等。
  • 小于. 对于第一个索引 i,其中 s1.value[i] != s2.value[i]: 如果 s1.value[i] < s2.value[i],则 s1 小于 s2

NULL values are compared following PostgreSQL's semantics. Lower nesting levels are used for tie-breaking.

Here are some queries returning true for the comparison.

SELECT {'k1': 2, 'k2': 3} < {'k1': 2, 'k2': 4} AS result;
SELECT {'k1': 'hello'} < {'k1': 'world'} AS result;

These queries return false.

SELECT {'k2': 4, 'k1': 3} < {'k2': 2, 'k1': 4} AS result;
SELECT {'k1': [4, 3]} < {'k1': [3, 6, 7]} AS result;

These queries return NULL.

SELECT {'k1': 2, 'k2': 3} < {'k1': 2, 'k2': NULL} AS result;

此查询返回一个Binder Error,因为键在位置上不匹配。

SELECT {'k1': 2, 'k2': 3} < {'k2': 2, 'k1': 4} AS result;
Binder Error: Cannot compare values of type STRUCT(k1 INTEGER, k2 INTEGER)
and type STRUCT(k2 INTEGER, k1 INTEGER) - an explicit cast is required

Functions

参见 Struct Functions