Structured data types

The Snowflake structured types are ARRAY, OBJECT, and MAP. Structured types contain elements or key-value pairs with specific Snowflake data types. The following are examples of structured types:

  • An ARRAY of INTEGER elements.

  • An OBJECT with VARCHAR and NUMBER key-value pairs.

  • A MAP that associates a VARCHAR key with a DOUBLE value.

You can use structured types in the following ways:

  • You can define a structured type column in an Apache Iceberg™ table.

    The Apache Iceberg™ data types list, struct, and map correspond to the structured ARRAY, structured OBJECT, and MAP types in Snowflake.

    A structured type column supports a maximum of 1000 sub-columns.

  • You use structured types when accessing data from a structured type column in an Iceberg table.

  • You can cast a semi-structured ARRAY, OBJECT, or VARIANT value to a corresponding structured type (for example, an ARRAY value to an ARRAY value of INTEGER elements). You can also cast a structured type of a semi-structured type.

Note

Currently, tables other than Iceberg tables don’t support structured types. You can’t add a column of a structured type to a regular table.

This topic explains how to use structured types in Snowflake.

Specifying a structured type

When defining a structured type column or casting a value to a structured type, use the syntax described in the following sections:

Specifying a structured ARRAY type

To specify a structured ARRAY type, use the following syntax:

ARRAY( <element_type> [ NOT NULL ] )
Copy

Where:

  • element_type is the Snowflake data type of the elements in this ARRAY.

    You can also specify a structured ARRAY, a structured OBJECT, or a MAP as the type of the element.

    Note

    In the definition of an Iceberg table column, you can’t specify a VARIANT, semi-structured ARRAY, or semi-structured OBJECT as the type of the ARRAY element.

  • NOT NULL specifies that the ARRAY can’t contain any elements that are NULL.

For example, compare the types returned by the SYSTEM$TYPEOF function in the following statement:

  • The first column expression casts a semi-structured ARRAY value to a structured ARRAY value (an ARRAY of NUMBER elements).

  • The second column expression specifies a semi-structured ARRAY value.

SELECT
  SYSTEM$TYPEOF(
    [1, 2, 3]::ARRAY(NUMBER)
  ) AS structured_array,
  SYSTEM$TYPEOF(
    [1, 2, 3]
  ) AS semi_structured_array;
Copy
+-------------------------------+-----------------------+
| STRUCTURED_ARRAY              | SEMI_STRUCTURED_ARRAY |
|-------------------------------+-----------------------|
| ARRAY(NUMBER(38,0))[LOB]      | ARRAY[LOB]            |
+-------------------------------+-----------------------+

Specifying a structured OBJECT type

To specify a structured OBJECT type, use the following syntax:

OBJECT(
  [
    <key> <value_type> [ NOT NULL ]
    [ , <key> <value_type> [ NOT NULL ] ]
    [ , ... ]
  ]
)
Copy

Where:

  • key specifies a key for the OBJECT type.

    • Each key in an OBJECT definition must be unique.

    • The order of the keys is part of the OBJECT definition. Comparing two OBJECT values that have the same keys in a different order isn’t allowed. (A compile time error occurs.)

    • If you don’t specify any key but specify the parentheses (that is, if you use OBJECT()), the resulting type is a structured OBJECT that contains no keys. A structured OBJECT with no keys is different from a semi-structured OBJECT.

  • value_type is the Snowflake data type of the value corresponding to the key.

    You can also specify a structured ARRAY, a structured OBJECT, or a MAP as the type of the value.

    Note

    In the definition of an Iceberg table column, you can’t specify a VARIANT, semi-structured ARRAY, or semi-structured OBJECT as the type of the value corresponding to the OBJECT key.

  • NOT NULL specifies that the value corresponding to the key can’t be NULL.

For example, compare the types returned by the SYSTEM$TYPEOF function in the following statement:

  • The first column expression casts a semi-structured OBJECT value to a structured OBJECT value that contains the following keys and values:

    • A key named str with a VARCHAR value that is not NULL.

    • A key named num with a NUMBER value.

  • The second column expression specifies a semi-structured OBJECT value.

SELECT
  SYSTEM$TYPEOF(
    {
      'str': 'test',
      'num': 1
    }::OBJECT(
      str VARCHAR NOT NULL,
      num NUMBER
    )
  ) AS structured_object,
  SYSTEM$TYPEOF(
    {
      'str': 'test',
      'num': 1
    }
  ) AS semi_structured_object;
Copy
+---------------------------------------------------------------+------------------------+
| STRUCTURED_OBJECT                                             | SEMI_STRUCTURED_OBJECT |
|---------------------------------------------------------------+------------------------|
| OBJECT(str VARCHAR(16777216) NOT NULL, num NUMBER(38,0))[LOB] | OBJECT[LOB]            |
+---------------------------------------------------------------+------------------------+

Specifying a MAP type

To specify a MAP type, use the following syntax:

MAP( <key_type> , <value_type> [ NOT NULL ] )
Copy

Where:

  • key_type is the Snowflake data type of the key for the map. You must use one of the following types for keys:

    • VARCHAR

    • NUMBER with the scale 0

    You can’t use a floating point data type as the type for the key.

    Map keys can’t be NULL.

  • value_type is the Snowflake data type of the values in the map.

    You can also specify a structured ARRAY, a structured OBJECT, or a MAP as the type of the values.

    Note

    In the definition of an Iceberg table column, you can’t specify a VARIANT, semi-structured ARRAY, or semi-structured OBJECT as the type of the value in the MAP.

  • NOT NULL specifies that the value corresponding to the key can’t be NULL.

The following example casts a semi-structured OBJECT value to a MAP value and uses the SYSTEM$TYPEOF function to print the resulting type of the value. The MAP associates VARCHAR keys with VARCHAR values.

SELECT
  SYSTEM$TYPEOF(
    {
      'a_key': 'a_val',
      'b_key': 'b_val'
    }::MAP(VARCHAR, VARCHAR)
  ) AS map_example;
Copy
+------------------------------------------------+
| MAP_EXAMPLE                                    |
|------------------------------------------------|
| MAP(VARCHAR(16777216), VARCHAR(16777216))[LOB] |
+------------------------------------------------+

Using structured types in semi-structured types

You can’t use a MAP, structured OBJECT, or structured ARRAY value in a VARIANT, semi-structured OBJECT, or semi-structured ARRAY value. An error occurs in the following situations:

Converting structured and semi-structured types

The following table summarizes rules for converting structured OBJECT, structured ARRAY, and MAP values to semi-structured OBJECT, ARRAY, and VARIANT values (and vice versa).

Source data type

Target data type

Castable

Coercible

Semi-structured ARRAY

Structured ARRAY

Semi-structured OBJECT

  • Structured OBJECT

  • MAP

Semi-structured VARIANT

  • Structured ARRAY

  • Structured OBJECT

  • MAP

Structured ARRAY

Semi-structured ARRAY

  • Structured OBJECT

  • MAP

Semi-structured OBJECT

  • Structured ARRAY

  • Structured OBJECT

  • MAP

Semi-structured VARIANT

The following sections explain these rules in more detail.

Explicitly casting a semi-structured type to a structured type

To explicitly cast a value of a semi-structured type to a value of a structured type, you can call the CAST function or use the :: operator.

Note

TRY_CAST isn’t supported for structured types.

You can only cast values of the following semi-structured types to values of the corresponding structured type; otherwise, a runtime error occurs.

Semi-structured type

Structured type that you can cast to

ARRAY

Structured ARRAY

OBJECT

MAP or structured OBJECT

VARIANT

MAP or structured ARRAY or OBJECT

The next sections provide more detail about how the types are cast:

Casting semi-structured ARRAY and VARIANT values to structured ARRAY values

The following steps demonstrate how to cast a semi-structured ARRAY or VARIANT value to an ARRAY value of NUMBER elements:

SELECT
  SYSTEM$TYPEOF(
    CAST ([1,2,3] AS ARRAY(NUMBER))
  ) AS array_cast_type,
  SYSTEM$TYPEOF(
    CAST ([1,2,3]::VARIANT AS ARRAY(NUMBER))
  ) AS variant_cast_type;
Copy

Or:

SELECT
  SYSTEM$TYPEOF(
    [1,2,3]::ARRAY(NUMBER)
  ) AS array_cast_type,
  SYSTEM$TYPEOF(
    [1,2,3]::VARIANT::ARRAY(NUMBER)
  ) AS variant_cast_type;
Copy
+--------------------------+--------------------------+
| ARRAY_CAST_TYPE          | VARIANT_CAST_TYPE        |
|--------------------------+--------------------------|
| ARRAY(NUMBER(38,0))[LOB] | ARRAY(NUMBER(38,0))[LOB] |
+--------------------------+--------------------------+

When you cast a semi-structured ARRAY or VARIANT value to a structured ARRAY value, note the following:

  • Each element of the ARRAY value is cast to the specified type of the ARRAY.

    Casting the ARRAY column to ARRAY(VARCHAR) converts each value to a VARCHAR value:

    SELECT
      CAST ([1,2,3] AS ARRAY(VARCHAR)) AS varchar_array,
      SYSTEM$TYPEOF(varchar_array) AS array_cast_type;
    
    Copy
    +---------------+-------------------------------+
    | VARCHAR_ARRAY | ARRAY_CAST_TYPE               |
    |---------------+-------------------------------|
    | [             | ARRAY(VARCHAR(16777216))[LOB] |
    |   "1",        |                               |
    |   "2",        |                               |
    |   "3"         |                               |
    | ]             |                               |
    +---------------+-------------------------------+
    
  • If the element can’t be cast to the specified type (for example, casting ['a', 'b', 'c'] to ARRAY(NUMBER)), the cast fails.

  • If the ARRAY value contains NULL elements and the ARRAY type specifies NOT NULL (for example, casting [1, NULL, 3] to ARRAY(NUMBER NOT NULL), the cast fails.

  • Elements that are JSON null values are converted to NULL, if the target element type doesn’t support JSON nulls (that is, the target type isn’t a semi-structured ARRAY, OBJECT, or VARIANT).

    For example, if you are casting to ARRAY(NUMBER), JSON null values are converted to NULL because NUMBER doesn’t support JSON nulls.

    On the other hand, if you are casting to ARRAY(VARIANT), JSON null values aren’t converted to NULL because VARIANT supports JSON nulls.

Casting semi-structured OBJECT and VARIANT values to structured OBJECT values

The following steps demonstrate how to cast a semi-structured OBJECT or VARIANT value to a structured OBJECT value containing the city and state key-value pairs (which are VARCHAR values):

SELECT
  SYSTEM$TYPEOF(
    CAST ({'city':'San Mateo','state':'CA'} AS OBJECT(city VARCHAR, state VARCHAR))
  ) AS object_cast_type,
  SYSTEM$TYPEOF(
    CAST ({'city':'San Mateo','state':'CA'}::VARIANT AS OBJECT(city VARCHAR, state VARCHAR))
  ) AS variant_cast_type;
Copy

Or:

SELECT
  SYSTEM$TYPEOF(
     {'city':'San Mateo','state':'CA'}::OBJECT(city VARCHAR, state VARCHAR)
  ) AS object_cast_type,
  SYSTEM$TYPEOF(
     {'city':'San Mateo','state':'CA'}::VARIANT::OBJECT(city VARCHAR, state VARCHAR)
  ) AS variant_cast_type;
Copy
+--------------------------------------------------------------+--------------------------------------------------------------+
| OBJECT_CAST_TYPE                                             | VARIANT_CAST_TYPE                                            |
|--------------------------------------------------------------+--------------------------------------------------------------|
| OBJECT(city VARCHAR(16777216), state VARCHAR(16777216))[LOB] | OBJECT(city VARCHAR(16777216), state VARCHAR(16777216))[LOB] |
+--------------------------------------------------------------+--------------------------------------------------------------+

When you cast a semi-structured OBJECT or VARIANT value to a structured OBJECT value, note the following:

  • The OBJECT value can’t contain any additional keys that aren’t specified in the OBJECT type.

    If there are additional keys, the cast fails.

  • If the OBJECT value is missing a key that is specified in the OBJECT type, the cast fails.

  • The value of each key in the OBJECT value is converted to the specified type for that key.

    If a value can’t be cast to the specified type, the cast fails.

  • If the value for a key is a JSON null value, the value is converted to NULL when the target value type doesn’t support JSON nulls (that is, the target type is not a semi-structured ARRAY, OBJECT, or VARIANT).

    For example, if you are casting to OBJECT(city VARCHAR), JSON null values are converted to NULL because VARCHAR doesn’t support JSON nulls.

    On the other hand, if you are casting to OBJECT(city VARIANT), JSON null values aren’t converted to NULL because VARIANT supports JSON nulls.

Casting semi-structured OBJECT and VARIANT values to MAP values

The following statements demonstrate how to cast a semi-structured OBJECT or VARIANT value to a MAP value that associates a VARCHAR key with a VARCHAR value:

SELECT
  SYSTEM$TYPEOF(
    CAST ({'my_key':'my_value'} AS MAP(VARCHAR, VARCHAR))
  ) AS map_cast_type,
  SYSTEM$TYPEOF(
    CAST ({'my_key':'my_value'} AS MAP(VARCHAR, VARCHAR))
  ) AS variant_cast_type;
Copy

Or:

SELECT
  SYSTEM$TYPEOF(
    {'my_key':'my_value'}::MAP(VARCHAR, VARCHAR)
  ) AS map_cast_type,
  SYSTEM$TYPEOF(
    {'my_key':'my_value'}::VARIANT::MAP(VARCHAR, VARCHAR)
  ) AS variant_cast_type;
Copy
+------------------------------------------------+------------------------------------------------+
| MAP_CAST_TYPE                                  | VARIANT_CAST_TYPE                              |
|------------------------------------------------+------------------------------------------------|
| MAP(VARCHAR(16777216), VARCHAR(16777216))[LOB] | MAP(VARCHAR(16777216), VARCHAR(16777216))[LOB] |
+------------------------------------------------+------------------------------------------------+

When you cast a semi-structured OBJECT or VARIANT value to a MAP value, note the following:

  • If the keys and values do not match the specified types, the keys and values are converted to the specified types.

  • If the keys and values can’t be cast to the specified types, the cast fails.

  • If the value for a key is a JSON null value, the value is converted to NULL when the target value type doesn’t support JSON nulls (that is, the target type is not a semi-structured ARRAY, OBJECT, or VARIANT).

    For example, if you are casting to MAP(VARCHAR, VARCHAR), JSON null values are converted to NULL because VARCHAR doesn’t support JSON nulls.

    On the other hand, if you are casting to MAP(VARCHAR, VARIANT), JSON null values aren’t converted to NULL because VARIANT supports JSON nulls.

Explicitly casting a structured type to a semi-structured type

To explicitly cast a value of a structured type to a value of a semi-structured type, you can call the CAST function, use the :: operator, or call one of the conversion functions (for example, TO_ARRAY, TO_OBJECT, or TO_VARIANT).

Note

TRY_CAST isn’t supported with structured types.

Structured type

Semi-structured type that you can cast to

Structured ARRAY

ARRAY

MAP or structured OBJECT

OBJECT

MAP, structured ARRAY, or structured OBJECT

VARIANT

For example:

  • If col_structured_array is ARRAY(VARCHAR) type:

    • CAST(col_structured_array AS ARRAY) returns a semi-structured ARRAY value.

    • CAST(col_structured_array AS VARIANT) returns a VARIANT value that holds a semi-structured ARRAY value.

  • If col_structured_object is OBJECT(name VARCHAR, state VARCHAR) type:

    • CAST(col_structured_object AS OBJECT) returns a semi-structured OBJECT value.

    • CAST(col_structured_object AS VARIANT) returns a VARIANT value that holds a semi-structured OBJECT value.

  • If col_map is MAP(VARCHAR, VARCHAR) type:

    • CAST(col_map AS OBJECT) returns a semi-structured OBJECT value.

    • CAST(col_map AS VARIANT) returns a VARIANT value that holds a semi-structured OBJECT value.

Note the following:

  • When you are casting to a semi-structured OBJECT value, the order of keys in the structured OBJECT value isn’t preserved.

  • When you are casting a structured OBJECT or MAP value to a semi-structured OBJECT or VARIANT value, any NULL values are converted to JSON null values.

    If you are casting a structured ARRAY value to a VARIANT value, NULL values are preserved as is.

    SELECT [1,2,NULL,3]::ARRAY(INTEGER)::VARIANT;
    
    Copy
    +---------------------------------------+
    | [1,2,NULL,3]::ARRAY(INTEGER)::VARIANT |
    |---------------------------------------|
    | [                                     |
    |   1,                                  |
    |   2,                                  |
    |   undefined,                          |
    |   3                                   |
    | ]                                     |
    +---------------------------------------+
    
  • If you are casting a MAP value that uses a NUMBER type for keys, the MAP keys are converted to strings in the returned OBJECT value.

Implicit casting a value (coercion)

The following rules apply to implicitly casting (coercion) from a value of one structured type to a value of another structured type:

  • A structured type value can be coerced to another structured type value if the two basic types are the same:

    • An ARRAY value of one type can be coerced to an ARRAY value of another type, provided that the first element type is coercible to the second element type.

      An element type can be coerced to another element type in either of the following cases:

      • Both types are numeric. The following cases are supported:

        • Both use the same numeric type but possibly differ in precision and/or scale.

        • Coercing NUMBER to FLOAT (and vice versa).

      • Both types are timestamps. The following cases are supported:

        • Both use the same type but possibly differ in precision.

        • Coercing TIMESTAMP_LTZ to TIMESTAMP_TZ (and vice versa).

      For example:

      • An ARRAY(NUMBER) value can be coerced to an ARRAY(DOUBLE) value.

      • An ARRAY(DATE) value can’t be coerced to an ARRAY(NUMBER) value.

    • An OBJECT value with one type definition can be coerced to an OBJECT value of with another type definition only if all of the following are true:

      • Both OBJECT types have the same number of keys.

      • Both OBJECT types use the same names for keys.

      • The keys in both OBJECT types are in the same order.

      • The type of each value in one OBJECT type can be coerced to the type of the corresponding value in the other OBJECT type.

        As is the case with element types in structured ARRAY values, you can coerce the type of one value to another type only if:

        • Both types are numeric. The following cases are supported:

          • Both use the same numeric type but possibly differ in precision and/or scale.

          • Coercing NUMBER to FLOAT (and vice versa).

        • Both types are timestamps. The following cases are supported:

          • Both use the same type but possibly differ in precision.

          • Coercing TIMESTAMP_LTZ to TIMESTAMP_TZ (and vice versa).

      For example:

      • An OBJECT(city VARCHAR, zipcode NUMBER) value can be coerced to an OBJECT(city VARCHAR, zipcode DOUBLE) value.

      • An OBJECT(city VARCHAR, zipcode NUMBER) value can’t be coerced to an OBJECT(city VARCHAR, zipcode DATE) value.

    • A MAP value with one value type can be coerced to a MAP value with a different value type if:

      • Both value types are numeric. The following cases are supported:

        • Both use the same numeric type but possibly differ in precision and/or scale.

        • Coercing NUMBER to FLOAT (and vice versa).

      • Both value types are timestamps. The following cases are supported:

        • Both use the same type but possibly differ in precision.

        • Coercing TIMESTAMP_LTZ to TIMESTAMP_TZ (and vice versa).

      For example, a MAP(VARCHAR, NUMBER) value can be coerced to a MAP(VARCHAR, DOUBLE) value.

    • A MAP value with one key type can be coerced to a MAP value with a different key type if both key types use the same integer NUMERIC type that differ only in precision.

      For example, a MAP(VARCHAR, NUMBER) value can’t be coerced to a MAP(NUMBER, NUMBER) value.

  • A structured type value can’t be coerced to a semi-structured value (and vice versa).

  • A VARCHAR value can’t be coerced to a structured type value.

Casting from one structured type to another

You can call the CAST function or use the :: operator to cast from a value of one structured type to a value of another structured type. You can cast values from and to the following structured types:

Note

TRY_CAST isn’t supported with structured types.

If it isn’t possible to cast the values from one type to the other, the cast fails. For example, attempting to cast an ARRAY(BOOLEAN) value to an ARRAY(DATE) value fails.

Example: Casting from one type of ARRAY value to another

The following example casts an ARRAY(NUMBER) value to an ARRAY(VARCHAR) value:

SELECT CAST(
  CAST([1,2,3] AS ARRAY(NUMBER))
  AS ARRAY(VARCHAR)) AS cast_array;
Copy
+------------+
| CAST_ARRAY |
|------------|
| [          |
|   "1",     |
|   "2",     |
|   "3"      |
| ]          |
+------------+

Example: Changing the order of key-value pairs in an OBJECT value

The following example changes the order of key-value pairs in a structured OBJECT value:

SELECT CAST(
  {'city': 'San Mateo','state': 'CA'}::OBJECT(city VARCHAR, state VARCHAR)
  AS OBJECT(state VARCHAR, city VARCHAR)) AS object_value_order;
Copy
+-----------------------+
| OBJECT_VALUE_ORDER    |
|-----------------------|
| {                     |
|   "state": "CA",      |
|   "city": "San Mateo" |
| }                     |
+-----------------------+

Example: Changing the key names in an OBJECT value

To change the key names in a structured OBJECT value, specify the RENAME FIELDS keywords at the end of CAST. For example:

SELECT CAST({'city':'San Mateo','state': 'CA'}::OBJECT(city VARCHAR, state VARCHAR)
  AS OBJECT(city_name VARCHAR, state_name VARCHAR) RENAME FIELDS) AS object_value_key_names;
Copy
+-----------------------------+
| OBJECT_VALUE_KEY_NAMES      |
|-----------------------------|
| {                           |
|   "city_name": "San Mateo", |
|   "state_name": "CA"        |
| }                           |
+-----------------------------+

Example: Adding keys to an OBJECT value

If the type that you are casting to has additional key-value pairs that aren’t present in the original structured OBJECT value, specify the ADD FIELDS keywords at the end of CAST. For example:

SELECT CAST({'city':'San Mateo','state': 'CA'}::OBJECT(city VARCHAR, state VARCHAR)
  AS OBJECT(city VARCHAR, state VARCHAR, zipcode NUMBER) ADD FIELDS) AS add_fields;
Copy
+------------------------+
| ADD_FIELDS             |
|------------------------|
| {                      |
|   "city": "San Mateo", |
|   "state": "CA",       |
|   "zipcode": null      |
| }                      |
+------------------------+

The values for the newly added keys are set to NULL. If you want to assign a value to these keys, call the OBJECT_INSERT function instead.

Constructing structured ARRAY, structured OBJECT, and MAP values

The following sections explain how to construct structured ARRAY, structured OBJECT, and MAP values.

Using SQL functions to construct structured ARRAY and OBJECT values

The following functions construct semi-structured ARRAY values:

The following functions construct semi-structured OBJECT values:

To construct a structured ARRAY or OBJECT value, use these functions and explicitly cast the return value of the function. For example:

SELECT ARRAY_CONSTRUCT(10, 20, 30)::ARRAY(NUMBER);
Copy
SELECT OBJECT_CONSTRUCT(
  'oname', 'abc',
  'created_date', '2020-01-18'::DATE
)::OBJECT(
  oname VARCHAR,
  created_date DATE
);
Copy

For details, refer to Explicitly casting a semi-structured type to a structured type.

Note

You can’t pass structured ARRAY, structured OBJECT, or MAP values to these functions. Doing so would result in a structured type being implicitly cast to a semi-structured type, which isn’t allowed, as noted in Implicit casting a value (coercion).

Using ARRAY and OBJECT constants to construct structured ARRAY and OBJECT values

When you specify an ARRAY constant or an OBJECT constant, you are specifying a semi-structured ARRAY or OBJECT value.

To construct a structured ARRAY or OBJECT value, you must explicitly cast the expression. For example:

SELECT [10, 20, 30]::ARRAY(NUMBER);
Copy
SELECT {
  'oname': 'abc',
  'created_date': '2020-01-18'::DATE
}::OBJECT(
  oname VARCHAR,
  created_date DATE
);
Copy

For details, refer to Explicitly casting a semi-structured type to a structured type.

Constructing a MAP value

To construct a MAP value, construct a semi-structured OBJECT value, and cast the OBJECT value to a MAP value.

For example, the following statements both produce the MAP value {'city'->'San Mateo','state'->'CA'}:

SELECT OBJECT_CONSTRUCT(
  'city', 'San Mateo',
  'state', 'CA'
)::MAP(
  VARCHAR,
  VARCHAR
);
Copy
SELECT {
  'city': 'San Mateo',
  'state': 'CA'
}::MAP(
  VARCHAR,
  VARCHAR
);
Copy

The following statement produces the MAP value {-10->'CA',-20->'OR'}:

SELECT {
  '-10': 'CA',
  '-20': 'OR'
}::MAP(
  NUMBER,
  VARCHAR
);
Copy

For details, refer to Casting semi-structured OBJECT and VARIANT values to MAP values.

Working with keys, values, and elements in values of structured types

The following sections explain how to use keys, values, and elements in values of structured types.

Getting the list of keys from a structured OBJECT value

To get the list of keys in a structured OBJECT value, call the OBJECT_KEYS function:

SELECT OBJECT_KEYS({'city':'San Mateo','state':'CA'}::OBJECT(city VARCHAR, state VARCHAR));
Copy

If the input is a structured OBJECT value, the function returns an ARRAY(VARCHAR) value containing the keys. If the input is a semi-structured OBJECT value, the function returns an ARRAY value.

Getting the list of keys from a MAP value

To get the list of keys in a MAP value, call the MAP_KEYS function:

SELECT MAP_KEYS({'my_key':'my_value'}::MAP(VARCHAR,VARCHAR));
Copy

Accessing values and elements from values of structured types

You can use the following methods to access values and elements from structured ARRAY, structured OBJECT, and MAP values:

The returned values and elements have the type specified for the structured value, rather than VARIANT.

The following example passes the first element of a semi-structured ARRAY value and an ARRAY(VARCHAR) value to the SYSTEM$TYPEOF function to return the data type of that element:

SELECT
  SYSTEM$TYPEOF(
    ARRAY_CONSTRUCT('San Mateo')[0]
  ) AS semi_structured_array_element,
  SYSTEM$TYPEOF(
    CAST(
      ARRAY_CONSTRUCT('San Mateo') AS ARRAY(VARCHAR)
    )[0]
  ) AS structured_array_element;
Copy
+-------------------------------+-----------------------------+
| SEMI_STRUCTURED_ARRAY_ELEMENT | STRUCTURED_ARRAY_ELEMENT    |
|-------------------------------+-----------------------------|
| VARIANT[LOB]                  | VARCHAR(16777216)[LOB]      |
+-------------------------------+-----------------------------+

Note the following:

  • When you pass a structured OBJECT value to the GET or GET_IGNORE_CASE function, you must specify a constant for the key.

    You don’t need to specify a constant if you are passing a MAP or structured ARRAY value to the GET function.

    You also don’t need to specify a constant if you are passing a MAP value to the GET_IGNORE_CASE function.

  • When you pass a structured OBJECT, structured ARRAY, or MAP value to the GET_PATH function, you must specify a constant for the path name.

  • For a structured OBJECT value, if you use an OBJECT key or a path that doesn’t exist, a compile-time error occurs.

    In contrast, when you use an index, key, or path that doesn’t exist with a semi-structured OBJECT value, the function returns NULL.

Determining the size of a structured ARRAY value

To determine the size of a structured ARRAY value, pass the ARRAY value to the ARRAY_SIZE function:

SELECT ARRAY_SIZE([1,2,3]::ARRAY(NUMBER));
Copy

Determining the size of a MAP value

To determine the size of a MAP value, pass the MAP value to MAP_SIZE function:

SELECT MAP_SIZE({'my_key':'my_value'}::MAP(VARCHAR,VARCHAR));
Copy

Looking up elements in a structured ARRAY value

To determine if an element is present in a structured ARRAY value, call the ARRAY_CONTAINS function. For example:

SELECT ARRAY_CONTAINS(10, [1, 10, 100]::ARRAY(NUMBER));
Copy

To determine the position of an element in a structured ARRAY value, call the ARRAY_POSITION function. For example:

SELECT ARRAY_POSITION(10, [1, 10, 100]::ARRAY(NUMBER));
Copy

Note

For both functions, use an element of a type that is comparable to the type of the ARRAY value.

Don’t cast the expression for the element to a VARIANT value.

Determining if a MAP value contains a key

To determine if a MAP value contains a key, call the MAP_CONTAINS_KEY function:

For example:

SELECT MAP_CONTAINS_KEY('key_to_find', my_map);
Copy
SELECT MAP_CONTAINS_KEY(10, my_map);
Copy

Comparing values

The following sections explain how to compare values:

Comparing structured values with semi-structured values

You can’t compare a structured ARRAY, structured OBJECT, or MAP value with a semi-structured ARRAY, OBJECT, or VARIANT value.

Comparing structured values with other structured values

You can compare two values of the same type (for example, two structured ARRAY values, two structured OBJECT values, or two MAP values).

Currently, the following comparison operators are supported for comparing values of structured types:

  • =

  • !=

  • <

  • <=

  • >=

  • >

When you compare two structured values for equality, note the following:

  • If one type can’t be coerced to the other type, the comparison fails.

  • When you compare MAP values that have numeric keys, the keys are compared as numbers (not as VARCHAR values).

When you compare two structured values using <, <=, >=, or >, the structured value fields are compared in alphabetical order. For example, the following value:

{'a':2,'b':1}::OBJECT(b INTEGER,a INTEGER)
Copy

is greater than:

{'a':1,'b':2}::OBJECT(b INTEGER,a INTEGER)
Copy

Determining if two ARRAY values overlap

To determine if the elements of two structured ARRAY values overlap, call the ARRAYS_OVERLAP function. For example:

SELECT ARRAYS_OVERLAP(numeric_array, other_numeric_array);
Copy

The ARRAY values must be of comparable types.

You can’t pass a semi-structured ARRAY value and a structured ARRAY value to this function. Both ARRAY values must either be structured or semi-structured.

Transforming values of structured types

The following sections explain how to transform structured ARRAY, structured OBJECT, and MAP values:

Transforming structured ARRAY values

When you pass a structured ARRAY value to these functions, the functions return a structured ARRAY value of the same type:

The next sections explain how these functions work with structured ARRAY values.

Functions that add elements to ARRAY values

The following functions add elements to an ARRAY values:

For these functions, the type of the element must be coercible to the type of the ARRAY value.

For example, the following call succeeds because a NUMBER value can be coerced to a DOUBLE value (the type of the ARRAY value):

SELECT ARRAY_APPEND( [1,2]::ARRAY(DOUBLE), 3::NUMBER );
Copy

The following call succeeds because VARCHAR values can be coerced to DOUBLE values:

SELECT ARRAY_APPEND( [1,2]::ARRAY(DOUBLE), '3' );
Copy

The following call fails because DATE values can’t be coerced to NUMBER values:

SELECT ARRAY_APPEND( [1,2]::ARRAY(NUMBER), '2022-02-02'::DATE );
Copy

Functions that accept multiple ARRAY values as input

The following functions accept multiple ARRAY values as input arguments:

When you call these functions, both arguments must either be structured ARRAY values or semi-structured ARRAY values. For example, the following calls fail because one argument is a structured ARRAY value and the other argument is a semi-structured ARRAY value:

SELECT ARRAY_CAT( [1,2]::ARRAY(NUMBER), ['3','4'] );
Copy
SELECT ARRAY_CAT( [1,2], ['3','4']::ARRAY(VARCHAR) );
Copy

The ARRAY_EXCEPT function returns an ARRAY value of the same type as the ARRAY value in the first argument.

The ARRAY_CAT and ARRAY_INTERSECTION functions return an ARRAY value of a type that can accommodate the types of both input values.

For example, the following call to ARRAY_CAT passes in two structured ARRAY values:

  • The first structured ARRAY value doesn’t allow NULLs and contains NUMBER values with the scale of 0 (NUMBER(38, 0)).

  • The second structured ARRAY value contains a NULL and a NUMBER value that has the scale of 1.

The ARRAY value returned by ARRAY_CAT allows NULLs and contains NUMBER values with the scale of 1.

SELECT
  ARRAY_CAT(
    [1, 2, 3]::ARRAY(NUMBER NOT NULL),
    [5.5, NULL]::ARRAY(NUMBER(2, 1))
  ) AS concatenated_array,
  SYSTEM$TYPEOF(concatenated_array);
Copy
+--------------------+-----------------------------------+
| CONCATENATED_ARRAY | SYSTEM$TYPEOF(CONCATENATED_ARRAY) |
|--------------------+-----------------------------------|
| [                  | ARRAY(NUMBER(38,1))[LOB]          |
|   1,               |                                   |
|   2,               |                                   |
|   3,               |                                   |
|   5.5,             |                                   |
|   undefined        |                                   |
| ]                  |                                   |
+--------------------+-----------------------------------+

For the ARRAY_CAT function, the ARRAY value in the second argument must be coercible to the type in the first argument.

For the ARRAY_EXCEPT and ARRAY_INTERSECTION functions, the ARRAY value in the second argument must be comparable to the ARRAY value in the first argument.

For example, the following call succeeds because an ARRAY(NUMBER) value is comparable to an ARRAY(DOUBLE) value:

SELECT ARRAY_EXCEPT( [1,2]::ARRAY(NUMBER), [2,3]::ARRAY(DOUBLE) );
Copy

The following call fails because an ARRAY(NUMBER) value isn’t comparable to an ARRAY(VARCHAR) value:

SELECT ARRAY_EXCEPT( [1,2]::ARRAY(NUMBER), ['2','3']::ARRAY(VARCHAR) );
Copy

Transforming structured OBJECT values

The following sections explain how to return a structured OBJECT value that has been transformed from another OBJECT value:

To change the order of key-value pairs, rename keys, or add keys without specifying values, use the CAST function or :: operator. For details, see Casting from one structured type to another.

Removing key-value pairs

To return a new OBJECT value that contains the key-value pairs from an existing OBJECT value with specific key-value pairs removed, call the OBJECT_DELETE function.

When calling this function, note the following:

  • For the arguments that are keys, you must specify constants.

  • If the specified key isn’t part of the OBJECT type definition, the call fails. For example, the following call fails because the OBJECT value doesn’t contain the specified key zip_code:

    SELECT OBJECT_DELETE( {'city':'San Mateo','state':'CA'}::OBJECT(city VARCHAR,state VARCHAR), 'zip_code' );
    
    Copy
    093201 (23001): Function OBJECT_DELETE: expected structured object to contain field zip_code but it did not.
    
  • The function returns a structured OBJECT value. The type of the OBJECT value excludes the deleted key. For example, suppose that you remove the city key:

    SELECT
      OBJECT_DELETE(
        {'city':'San Mateo','state':'CA'}::OBJECT(city VARCHAR,state VARCHAR),
        'city'
      ) AS new_object,
      SYSTEM$TYPEOF(new_object);
    
    Copy

    The function returns an OBJECT value of the type OBJECT(state VARCHAR), which doesn’t include the city key.

    +-----------------+--------------------------------------+
    | NEW_OBJECT      | SYSTEM$TYPEOF(NEW_OBJECT)            |
    |-----------------+--------------------------------------|
    | {               | OBJECT(state VARCHAR(16777216))[LOB] |
    |   "state": "CA" |                                      |
    | }               |                                      |
    +-----------------+--------------------------------------+
    
  • If the function removes all keys from the OBJECT value, the function returns an empty structured OBJECT value of the type OBJECT().

    SELECT
      OBJECT_DELETE(
        {'state':'CA'}::OBJECT(state VARCHAR),
        'state'
      ) AS new_object,
      SYSTEM$TYPEOF(new_object);
    
    Copy
    +------------+---------------------------+
    | NEW_OBJECT | SYSTEM$TYPEOF(NEW_OBJECT) |
    |------------+---------------------------|
    | {}         | OBJECT()[LOB]             |
    +------------+---------------------------+
    

    When the type of a structured OBJECT value includes key-value pairs, the names and types of those pairs are included in parentheses in the type (for example, OBJECT(city VARCHAR)). Because an empty structured OBJECT value contains no key-value pairs, the parentheses are empty.

Inserting key-value pairs and updating values

To return a new OBJECT value that contains the key-value pairs from an existing OBJECT value with additional key-value pairs or new values for keys, call the OBJECT_INSERT function.

When calling this function, note the following:

  • For the arguments that are keys, you must specify constants.

  • When the updateFlag argument is FALSE (when you are inserting a new key-value pair):

    • If you specify a key that already exists in the OBJECT value, an error occurs.

      SELECT OBJECT_INSERT(
        {'city':'San Mateo','state':'CA'}::OBJECT(city VARCHAR,state VARCHAR),
        'city',
        'San Jose',
        false
      );
      
      Copy
      093202 (23001): Function OBJECT_INSERT:
        expected structured object to not contain field city but it did.
      
    • The function returns a structured OBJECT value. The type of the OBJECT value includes the newly inserted key. For example, suppose that you add the zipcode key with the FLOAT value 94402:

      SELECT
        OBJECT_INSERT(
          {'city':'San Mateo','state':'CA'}::OBJECT(city VARCHAR,state VARCHAR),
          'zip_code',
          94402::FLOAT,
          false
        ) AS new_object,
        SYSTEM$TYPEOF(new_object) AS type;
      
      Copy
      +-------------------------------------+---------------------------------------------------------------------------------------+
      | NEW_OBJECT                          | TYPE                                                                                  |
      |-------------------------------------+---------------------------------------------------------------------------------------|
      | {                                   | OBJECT(city VARCHAR(16777216), state VARCHAR(16777216), zip_code FLOAT NOT NULL)[LOB] |
      |   "city": "San Mateo",              |                                                                                       |
      |   "state": "CA",                    |                                                                                       |
      |   "zip_code": 9.440200000000000e+04 |                                                                                       |
      | }                                   |                                                                                       |
      +-------------------------------------+---------------------------------------------------------------------------------------+
      

      The type of the inserted value determines the type added to the OBJECT type definition. In this case, the value for zipcode is a value cast to a FLOAT, so the type of zipcode is FLOAT.

  • When the updateFlag argument is TRUE (when you are replacing an existing key-value pair):

    • If you specify a key that doesn’t exist in the OBJECT value, an error occurs.

    • The function returns a structured OBJECT value of the same type.

    • The type of the inserted value is coerced to the type of the existing key.

Selecting key-value pairs from an existing OBJECT

To return a new OBJECT value that contains selected key-value pairs from an existing OBJECT value, call the OBJECT_PICK function.

When calling this function, note the following:

  • For the arguments that are keys, you must specify constants.

  • You can’t pass in an ARRAY of keys as the second argument. You must specify each key as a separate argument.

  • The function returns a structured OBJECT value. The type of the OBJECT value includes the keys in the order in which they are specified.

    For example, suppose that you select the state and city keys in that order:

    SELECT
      OBJECT_PICK(
        {'city':'San Mateo','state':'CA','zip_code':94402}::OBJECT(city VARCHAR,state VARCHAR,zip_code DOUBLE),
        'state',
        'city') AS new_object,
      SYSTEM$TYPEOF(new_object);
    
    Copy

    The function returns an OBJECT value of the type OBJECT(state VARCHAR, city VARCHAR).

    +-----------------------+--------------------------------------------------------------+
    | NEW_OBJECT            | SYSTEM$TYPEOF(NEW_OBJECT)                                    |
    |-----------------------+--------------------------------------------------------------|
    | {                     | OBJECT(state VARCHAR(16777216), city VARCHAR(16777216))[LOB] |
    |   "state": "CA",      |                                                              |
    |   "city": "San Mateo" |                                                              |
    | }                     |                                                              |
    +-----------------------+--------------------------------------------------------------+
    

Transforming MAP values

To transform MAP values, use the following functions:

Working with structured types

The following sections explain how to use different SQL functions and set operators with values of structured types:

Using the FLATTEN function with values of structured types

You can pass structured ARRAY, structured OBJECT, and MAP values to the FLATTEN function. As is the case with semi-structured data types, you can use the PATH argument to specify the value being flattened.

  • If the value being flattened is a structured ARRAY value and the RECURSIVE argument is FALSE, the value column contains a value of the same type as the ARRAY value.

    For example:

    SELECT value, SYSTEM$TYPEOF(value)
      FROM TABLE(FLATTEN(INPUT => [1.08, 2.13, 3.14]::ARRAY(DOUBLE)));
    
    Copy
    +-------+----------------------+
    | VALUE | SYSTEM$TYPEOF(VALUE) |
    |-------+----------------------|
    |  1.08 | FLOAT[DOUBLE]        |
    |  2.13 | FLOAT[DOUBLE]        |
    |  3.14 | FLOAT[DOUBLE]        |
    +-------+----------------------+
    
  • If the value being flattened is a MAP value and the RECURSIVE argument is FALSE, the key column contains a key of the same type as the MAP key, and the value column contains a value of the same type as the MAP value.

    For example:

    SELECT key, SYSTEM$TYPEOF(key), value, SYSTEM$TYPEOF(value)
      FROM TABLE(FLATTEN(INPUT => {'my_key': 'my_value'}::MAP(VARCHAR, VARCHAR)));
    
    Copy
    +--------+------------------------+----------+------------------------+
    | KEY    | SYSTEM$TYPEOF(KEY)     | VALUE    | SYSTEM$TYPEOF(VALUE)   |
    |--------+------------------------+----------+------------------------|
    | my_key | VARCHAR(16777216)[LOB] | my_value | VARCHAR(16777216)[LOB] |
    +--------+------------------------+----------+------------------------+
    
  • Otherwise, the key and value columns have the type VARIANT.

For MAP values, the order of keys and values returned is indeterminate.

Using the PARSE_JSON function

The PARSE_JSON function doesn’t return structured types.

Using structured types with set operators and CASE expressions

You can use structured ARRAY, structured OBJECT, and MAP values in:

For set operators, if different types are used in the different expressions (for example, if one type is ARRAY(NUMBER) and the other is ARRAY(DOUBLE)), one type is coerced to the other.

Working with other semi-structured functions

The following functions don’t accept a structured ARRAY, structured OBJECT, or MAP values as an input argument:

Passing a structured type value as input results in an error.

Accessing structured types in applications using drivers

In applications that use drivers (for example, the ODBC or JDBC driver, the Snowflake Connector for Python, etc.), structured type values are returned as semi-structured type values. For example:

  • The values in a structured ARRAY column are returned as semi-structured ARRAY values to the client application.

  • The values in a structured OBJECT or MAP column are returned as semi-structured OBJECT values to the client application.

Note

For client applications that use the JDBC driver, the ResultSet.getArray() method returns an error if the query results you want to retrieve contain a structured ARRAY value with NULL values.

To retrieve a string representation instead, use the ResultSet.getString() method:

String result = resultSet.getString(1);
Copy

Using structured types with user-defined functions (UDFs) and stored procedures

When you create a user-defined function (UDF), user-defined table function (UDTF), or stored procedure in SQL, Snowflake Scripting, Java, Python, or Scala, you can use structured types in the arguments and return values. For example:

CREATE OR REPLACE FUNCTION my_udf(
    location OBJECT(city VARCHAR, zipcode NUMBER, val ARRAY(BOOLEAN)))
  RETURNS VARCHAR
  AS
  $$
    ...
  $$;
Copy
CREATE OR REPLACE FUNCTION my_udtf(check BOOLEAN)
  RETURNS TABLE(col1 ARRAY(VARCHAR))
  AS
  $$
  ...
  $$;
Copy
CREATE OR REPLACE PROCEDURE my_procedure(values ARRAY(INTEGER))
  RETURNS ARRAY(INTEGER)
  LANGUAGE SQL
  AS
  $$
    ...
  $$;
Copy
CREATE OR REPLACE FUNCTION my_function(values ARRAY(INTEGER))
  RETURNS ARRAY(INTEGER)
  LANGUAGE PYTHON
  RUNTIME_VERSION=3.10
  AS
  $$
    ...
  $$;
Copy

Note

Structured types aren’t yet supported in UDFs, UDTFs, and stored procedures in JavaScript.

Viewing information about structured types

The following sections describe the views and commands that you can use to view information about structured types:

Using the SHOW COLUMNS command to view structured type information

In the output of the SHOW COLUMNS command, the data_type column includes information about the types of elements, keys, and values.

Using the DESCRIBE and other SHOW commands to view structured type information

The output of the following commands includes information about structured types:

For example, in the DESCRIBE RESULT output, the row for a MAP(VARCHAR, VARCHAR) column contains the following value in the type column:

map(VARCHAR(16777216), VARCHAR(16777216))
Copy

The row for an ARRAY(NUMBER) column contains the following value in the type column:

ARRAY(NUMBER(38,0))
Copy

Viewing information about the structured types used in a database

For columns of structured types, the INFORMATION_SCHEMA COLUMNS view only provides information about the basic data type of the column (ARRAY, OBJECT, or MAP).

For example, the data_type column just contains ARRAY, OBJECT, or MAP. The column doesn’t include the types of the elements, keys, or values.

To view information about the types of elements, keys, and values, use the following views: