Skip to content

Latest commit

 

History

History
3577 lines (2862 loc) · 106 KB

operators.md

File metadata and controls

3577 lines (2862 loc) · 106 KB

Operators

ZetaSQL supports operators. Operators are represented by special characters or keywords; they do not use function call syntax. An operator manipulates any number of data inputs, also called operands, and returns a result.

Common conventions:

  • Unless otherwise specified, all operators return NULL when one of the operands is NULL.
  • All operators will throw an error if the computation result overflows.
  • For all floating point operations, +/-inf and NaN may only be returned if one of the operands is +/-inf or NaN. In other cases, an error is returned.

Operator precedence

The following table lists all ZetaSQL operators from highest to lowest precedence, i.e., the order in which they will be evaluated within a statement.

<tr>
  <td>&nbsp;</td>
  <td>Array elements field access operator</td>
  <td><code>ARRAY</code></td>
  <td>Field access operator for elements in an array</td>
  <td>Binary</td>
</tr>

<tr>
  <td>&nbsp;</td>
  <td>Array subscript operator</td>
  <td><code>ARRAY</code></td>
  <td>Array position. Must be used with <code>OFFSET</code> or <code>ORDINAL</code>&mdash;see
  <a href="/google/zetasql/blob/master/docs/array_functions.md">Array Functions</a>

.

<tr>
  <td>&nbsp;</td>
  <td>JSON subscript operator</td>
  <td><code>JSON</code></td>
  <td>Field name or array position in JSON.</td>
  <td>Binary</td>
</tr>

<tr>
  <td>2</td>
  <td><code>+</code></td>
  <td>All numeric types</td>
  <td>Unary plus</td>
  <td>Unary</td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td><code>-</code></td>
  <td>All numeric types</td>
  <td>Unary minus</td>
  <td>Unary</td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td><code>~</code></td>
  <td>Integer or <code>BYTES</code></td>
  <td>Bitwise not</td>
  <td>Unary</td>
</tr>
<tr>
  <td>3</td>
  <td><code>*</code></td>
  <td>All numeric types</td>
  <td>Multiplication</td>
  <td>Binary</td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td><code>/</code></td>
  <td>All numeric types</td>
  <td>Division</td>
  <td>Binary</td>
</tr>

<tr>
  <td>&nbsp;</td>
  <td><code>||</code></td>
  <td><code>STRING</code>, <code>BYTES</code>, or <code>ARRAY&#60;T&#62;</code></td>
  <td>Concatenation operator</td>
  <td>Binary</td>
</tr>

<tr>
  <td>4</td>
  <td><code>+</code></td>
  <td>
    All numeric types, <code>DATE</code> with
    <code>INT64</code>
    , <code>INTERVAL</code>
  </td>
  <td>Addition</td>
  <td>Binary</td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td><code>-</code></td>
  <td>
    All numeric types, <code>DATE</code> with
    <code>INT64</code>
    , <code>INTERVAL</code>
  </td>
  <td>Subtraction</td>
  <td>Binary</td>
</tr>
<tr>
  <td>5</td>
  <td><code>&lt;&lt;</code></td>
  <td>Integer or <code>BYTES</code></td>
  <td>Bitwise left-shift</td>
  <td>Binary</td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td><code>&gt;&gt;</code></td>
  <td>Integer or <code>BYTES</code></td>
  <td>Bitwise right-shift</td>
  <td>Binary</td>
</tr>
<tr>
  <td>6</td>
  <td><code>&amp;</code></td>
  <td>Integer or <code>BYTES</code></td>
  <td>Bitwise and</td>
  <td>Binary</td>
</tr>
<tr>
  <td>7</td>
  <td><code>^</code></td>
  <td>Integer or <code>BYTES</code></td>
  <td>Bitwise xor</td>
  <td>Binary</td>
</tr>
<tr>
  <td>8</td>
  <td><code>|</code></td>
  <td>Integer or <code>BYTES</code></td>
  <td>Bitwise or</td>
  <td>Binary</td>
</tr>
<tr>
  <td>9 (Comparison Operators)</td>
  <td><code>=</code></td>
  <td>Any comparable type. See
  <a href="/google/zetasql/blob/master/docs/data-types.md">Data Types</a>

  for a complete list.</td>
  <td>Equal</td>
  <td>Binary</td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td><code>&lt;</code></td>
  <td>Any comparable type. See
  <a href="/google/zetasql/blob/master/docs/data-types.md">Data Types</a>

  for a complete list.</td>
  <td>Less than</td>
  <td>Binary</td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td><code>&gt;</code></td>
  <td>Any comparable type. See
  <a href="/google/zetasql/blob/master/docs/data-types.md">Data Types</a>

  for a complete list.</td>
  <td>Greater than</td>
  <td>Binary</td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td><code>&lt;=</code></td>
  <td>Any comparable type. See
  <a href="/google/zetasql/blob/master/docs/data-types.md">Data Types</a>

  for a complete list.</td>
  <td>Less than or equal to</td>
  <td>Binary</td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td><code>&gt;=</code></td>
  <td>Any comparable type. See
  <a href="/google/zetasql/blob/master/docs/data-types.md">Data Types</a>

  for a complete list.</td>
  <td>Greater than or equal to</td>
  <td>Binary</td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td><code>!=</code>, <code>&lt;&gt;</code></td>
  <td>Any comparable type. See
  <a href="/google/zetasql/blob/master/docs/data-types.md">Data Types</a>

  for a complete list.</td>
  <td>Not equal</td>
  <td>Binary</td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td><code>[NOT] LIKE</code></td>
  <td><code>STRING</code> and <code>BYTES</code></td>
  <td>Value does [not] match the pattern specified</td>
  <td>Binary</td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td><code>[NOT] BETWEEN</code></td>
  <td>Any comparable types. See
  <a href="/google/zetasql/blob/master/docs/data-types.md">Data Types</a>

  for a complete list.</td>
  <td>Value is [not] within the range specified</td>
  <td>Binary</td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td><code>[NOT] IN</code></td>
  <td>Any comparable types. See
  <a href="/google/zetasql/blob/master/docs/data-types.md">Data Types</a>

  for a complete list.</td>
  <td>Value is [not] in the set of values specified</td>
  <td>Binary</td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td><code>IS [NOT] NULL</code></td>
  <td>All</td>
  <td>Value is [not] <code>NULL</code></td>
  <td>Unary</td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td><code>IS [NOT] TRUE</code></td>
  <td><code>BOOL</code></td>
  <td>Value is [not] <code>TRUE</code>.</td>
  <td>Unary</td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td><code>IS [NOT] FALSE</code></td>
  <td><code>BOOL</code></td>
  <td>Value is [not] <code>FALSE</code>.</td>
  <td>Unary</td>
</tr>
<tr>
  <td>10</td>
  <td><code>NOT</code></td>
  <td><code>BOOL</code></td>
  <td>Logical <code>NOT</code></td>
  <td>Unary</td>
</tr>
<tr>
  <td>11</td>
  <td><code>AND</code></td>
  <td><code>BOOL</code></td>
  <td>Logical <code>AND</code></td>
  <td>Binary</td>
</tr>
<tr>
  <td>12</td>
  <td><code>OR</code></td>
  <td><code>BOOL</code></td>
  <td>Logical <code>OR</code></td>
  <td>Binary</td>
</tr>
Order of Precedence Operator Input Data Types Name Operator Arity
1 Field access operator

STRUCT
PROTO
JSON

Field access operator Binary
Binary
  Quantified LIKE STRING and BYTES Checks a search value for matches against several patterns. Binary

For example, the logical expression:

x OR y AND z

is interpreted as:

( x OR ( y AND z ) )

Operators with the same precedence are left associative. This means that those operators are grouped together starting from the left and moving right. For example, the expression:

x AND y AND z

is interpreted as:

( ( x AND y ) AND z )

The expression:

x * y / z

is interpreted as:

( ( x * y ) / z )

All comparison operators have the same priority, but comparison operators are not associative. Therefore, parentheses are required to resolve ambiguity. For example:

(x < y) IS FALSE

Operator list

Name Summary
Field access operator Gets the value of a field.
Array subscript operator Gets a value from an array at a specific position.
Struct subscript operator Gets the value of a field at a selected position in a struct.
JSON subscript operator Gets a value of an array element or field in a JSON expression.
Protocol buffer map subscript operator Gets the value in a protocol buffer map for a given key.
Array elements field access operator Traverses through the levels of a nested data type inside an array.
Arithmetic operators Performs arithmetic operations.
Date arithmetics operators Performs arithmetic operations on dates.
Datetime subtraction Computes the difference between two datetimes as an interval.
Interval arithmetic operators Adds an interval to a datetime or subtracts an interval from a datetime.
Bitwise operators Performs bit manipulation.
Logical operators Tests for the truth of some condition and produces TRUE, FALSE, or NULL.
Graph concatenation operator Combines multiple graph paths into one and preserves the original order of the nodes and edges.
Graph logical operators Tests for the truth of a condition in a graph and produces either TRUE or FALSE.
Graph predicates Tests for the truth of a condition for a graph element and produces TRUE, FALSE, or NULL.
IS DESTINATION predicate In a graph, checks to see if a node is or isn't the destination of an edge.
IS SOURCE predicate In a graph, checks to see if a node is or isn't the source of an edge.
PROPERTY_EXISTS predicate In a graph, checks to see if a property exists for an element.
SAME predicate In a graph, determines if all graph elements in a list bind to the same node or edge.
Comparison operators Compares operands and produces the results of the comparison as a BOOL value.
EXISTS operator Checks if a subquery produces one or more rows.
IN operator Checks for an equal value in a set of values.
IS operators Checks for the truth of a condition and produces either TRUE or FALSE.
IS DISTINCT FROM operator Checks if values are considered to be distinct from each other.
LIKE operator Checks if values are like or not like one another.
Quantified LIKE operator Checks a search value for matches against several patterns.
NEW operator Creates a protocol buffer.
Concatenation operator Combines multiple values into one.
WITH expression Creates variables for re-use and produces a result expression.

Field access operator

expression.fieldname[. ...]

Description

Gets the value of a field. Alternatively known as the dot operator. Can be used to access nested fields. For example, expression.fieldname1.fieldname2.

Input values:

  • STRUCT
  • PROTO
  • JSON
  • GRAPH_ELEMENT

Note: If the field to access is within a STRUCT, you can use the struct subscript operator to access the field by its position within the STRUCT instead of by its name. Accessing by a field by position is useful when fields are un-named or have ambiguous names.

Return type

  • For STRUCT: SQL data type of fieldname. If a field is not found in the struct, an error is thrown.
  • For PROTO: SQL data type of fieldname. If a field is not found in the protocol buffer, an error is thrown.
  • For JSON: JSON. If a field is not found in a JSON value, a SQL NULL is returned.
  • For GRAPH_ELEMENT: SQL data type of fieldname. If a field (property) is not found in the graph element, an error is produced.

Example

In the following example, the field access operations are .address and .country.

SELECT
  STRUCT(
    STRUCT('Yonge Street' AS street, 'Canada' AS country)
      AS address).address.country

/*---------*
 | country |
 +---------+
 | Canada  |
 *---------*/

Array subscript operator

Note: Syntax wrapped in double quotes ("") is required.

array_expression "[" array_subscript_specifier "]"

array_subscript_specifier:
  { index | position_keyword(index) }

position_keyword:
  { OFFSET | SAFE_OFFSET | ORDINAL | SAFE_ORDINAL }

Description

Gets a value from an array at a specific position.

Input values:

  • array_expression: The input array.
  • position_keyword(index): Determines where the index for the array should start and how out-of-range indexes are handled. The index is an integer that represents a specific position in the array.
    • OFFSET(index): The index starts at zero. Produces an error if the index is out of range. To produce NULL instead of an error, use SAFE_OFFSET(index). This position keyword produces the same result as index by itself.
    • SAFE_OFFSET(index): The index starts at zero. Returns NULL if the index is out of range.
    • ORDINAL(index): The index starts at one. Produces an error if the index is out of range. To produce NULL instead of an error, use SAFE_ORDINAL(index).
    • SAFE_ORDINAL(index): The index starts at one. Returns NULL if the index is out of range.
  • index: An integer that represents a specific position in the array. If used by itself without a position keyword, the index starts at zero and produces an error if the index is out of range. To produce NULL instead of an error, use the SAFE_OFFSET(index) or SAFE_ORDINAL(index) position keyword.

Return type

T where array_expression is ARRAY<T>.

Examples

In following query, the array subscript operator is used to return values at specific position in item_array. This query also shows what happens when you reference an index (6) in an array that is out of range. If the SAFE prefix is included, NULL is returned, otherwise an error is produced.

SELECT
  ["coffee", "tea", "milk"] AS item_array,
  ["coffee", "tea", "milk"][0] AS item_index,
  ["coffee", "tea", "milk"][OFFSET(0)] AS item_offset,
  ["coffee", "tea", "milk"][ORDINAL(1)] AS item_ordinal,
  ["coffee", "tea", "milk"][SAFE_OFFSET(6)] AS item_safe_offset

/*---------------------+------------+-------------+--------------+------------------*
 | item_array          | item_index | item_offset | item_ordinal | item_safe_offset |
 +---------------------+------------+-------------+--------------+------------------+
 | [coffee, tea, milk] | coffee     | coffee      | coffee       | NULL             |
 *----------------------------------+-------------+--------------+------------------*/

When you reference an index that is out of range in an array, and a positional keyword that begins with SAFE is not included, an error is produced. For example:

-- Error. Array index 6 is out of bounds.
SELECT ["coffee", "tea", "milk"][6] AS item_offset
-- Error. Array index 6 is out of bounds.
SELECT ["coffee", "tea", "milk"][OFFSET(6)] AS item_offset

Struct subscript operator

Note: Syntax wrapped in double quotes ("") is required.

struct_expression "[" struct_subscript_specifier "]"

struct_subscript_specifier:
  { index | position_keyword(index) }

position_keyword:
  { OFFSET | ORDINAL }

Description

Gets the value of a field at a selected position in a struct.

Input types

  • struct_expression: The input struct.
  • position_keyword(index): Determines where the index for the struct should start and how out-of-range indexes are handled. The index is an integer literal or constant that represents a specific position in the struct.
    • OFFSET(index): The index starts at zero. Produces an error if the index is out of range. Produces the same result as index by itself.
    • ORDINAL(index): The index starts at one. Produces an error if the index is out of range.
  • index: An integer literal or constant that represents a specific position in the struct. If used by itself without a position keyword, the index starts at zero and produces an error if the index is out of range.

Note: The struct subscript operator doesn't support SAFE positional keywords at this time.

Examples

In following query, the struct subscript operator is used to return values at specific locations in item_struct using position keywords. This query also shows what happens when you reference an index (6) in an struct that is out of range.

SELECT
  STRUCT<INT64, STRING, BOOL>(23, "tea", FALSE)[0] AS field_index,
  STRUCT<INT64, STRING, BOOL>(23, "tea", FALSE)[OFFSET(0)] AS field_offset,
  STRUCT<INT64, STRING, BOOL>(23, "tea", FALSE)[ORDINAL(1)] AS field_ordinal

/*-------------+--------------+---------------*
 | field_index | field_offset | field_ordinal |
 +-------------+--------------+---------------+
 | 23          | 23           | 23            |
 *-------------+--------------+---------------*/

When you reference an index that is out of range in a struct, an error is produced. For example:

-- Error: Field ordinal 6 is out of bounds in STRUCT
SELECT STRUCT<INT64, STRING, BOOL>(23, "tea", FALSE)[6] AS field_offset
-- Error: Field ordinal 6 is out of bounds in STRUCT
SELECT STRUCT<INT64, STRING, BOOL>(23, "tea", FALSE)[OFFSET(6)] AS field_offset

JSON subscript operator

Note: Syntax wrapped in double quotes ("") is required.

json_expression "[" array_element_id "]"
json_expression "[" field_name "]"

Description

Gets a value of an array element or field in a JSON expression. Can be used to access nested data.

Input values:

  • JSON expression: The JSON expression that contains an array element or field to return.
  • [array_element_id]: An INT64 expression that represents a zero-based index in the array. If a negative value is entered, or the value is greater than or equal to the size of the array, or the JSON expression doesn't represent a JSON array, a SQL NULL is returned.
  • [field_name]: A STRING expression that represents the name of a field in JSON. If the field name is not found, or the JSON expression is not a JSON object, a SQL NULL is returned.

Return type

JSON

Example

In the following example:

  • json_value is a JSON expression.
  • .class is a JSON field access.
  • .students is a JSON field access.
  • [0] is a JSON subscript expression with an element offset that accesses the zeroth element of an array in the JSON value.
  • ['name'] is a JSON subscript expression with a field name that accesses a field.
SELECT json_value.class.students[0]['name'] AS first_student
FROM
  UNNEST(
    [
      JSON '{"class" : {"students" : [{"name" : "Jane"}]}}',
      JSON '{"class" : {"students" : []}}',
      JSON '{"class" : {"students" : [{"name" : "John"}, {"name": "Jamie"}]}}'])
    AS json_value;

/*-----------------*
 | first_student   |
 +-----------------+
 | "Jane"          |
 | NULL            |
 | "John"          |
 *-----------------*/

Protocol buffer map subscript operator

proto_map_field_expression[proto_subscript_specifier]

proto_subscript_specifier:
  key_name | key_keyword(key_name)

key_keyword:
  { KEY | SAFE_KEY }

Description

Returns the value in a protocol buffer map for a given key.

Input values:

  • proto_map_field_expression: A protocol buffer map field.
  • key_keyword(key_name): Determines whether to produce NULL or an error if the key is not present in the protocol buffer map field.
    • KEY(key_name): Returns an error if the key is not present in the protocol buffer map field.
    • SAFE_KEY(key_name): Returns NULL if the key is not present in the protocol buffer map field.
    • key_name: When key_name is provided without a wrapping keyword, it is the same as KEY(key_name).
  • key_name: The key in the protocol buffer map field. This operator returns NULL if the key is NULL.

Return type

In the input protocol buffer map field, V as represented in map<K,V>.

Examples

To illustrate the use of this function, we use the protocol buffer message Item.

message Item {
  optional map<string, int64> purchased = 1;
};

In the following example, the subscript operator returns the value when the key is present.

SELECT
  m.purchased[KEY('A')] AS map_value
FROM
  (SELECT AS VALUE CAST("purchased { key: 'A' value: 2 }" AS Item)) AS m;

/*-----------*
 | map_value |
 +-----------+
 | 2         |
 *-----------*/

When the key does not exist in the map field and you use KEY, an error is produced. For example:

-- ERROR: Key not found in map: 2
SELECT
  m.purchased[KEY('B')] AS value
FROM
  (SELECT AS VALUE CAST("purchased { key: 'A' value: 2 }" AS Item)) AS m;

When the key does not exist in the map field and you use SAFE_KEY, the subscript operator returns NULL. For example:

SELECT
  CAST(m.purchased[SAFE_KEY('B')] AS safe_key_missing
FROM
  (SELECT AS VALUE CAST("purchased { key: 'A' value: 2 }" AS Item)) AS m;

/*------------------*
 | safe_key_missing |
 +------------------+
 | NULL             |
 *------------------*/

The subscript operator returns NULL when the map field or key is NULL. For example:

SELECT
  CAST(NULL AS Item).purchased[KEY('A')] AS null_map,
  m.purchased[KEY(NULL)] AS null_key
FROM
  (SELECT AS VALUE CAST("purchased { key: 'A' value: 2 }" AS Item)) AS m;

/*-----------------------*
 | null_map  | null_key  |
 +-----------------------+
 | NULL      | NULL      |
 *-----------------------*/

When a key is used without KEY() or SAFE_KEY(), it has the same behavior as if KEY() had been used. For example:

SELECT
  m.purchased['A'] AS map_value
FROM
  (SELECT AS VALUE CAST("purchased { key: 'A' value: 2 }" AS Item)) AS m;

/*-----------*
 | map_value |
 +-----------+
 | 2         |
 *-----------*/

Array elements field access operator

Note: Syntax wrapped in double quotes ("") is required.

array_expression.field_or_element[. ...]

field_or_element:
  { fieldname | array_element }

array_element:
  array_fieldname "[" array_subscript_specifier "]"

Description

The array elements field access operation lets you traverse through the levels of a nested data type inside an array.

Input values:

  • array_expression: An expression that evaluates to an array value.

  • field_or_element[. ...]: The field to access. This can also be a position in an array-typed field.

  • fieldname: The name of the field to access.

    For example, this query returns all values for the items field inside of the my_array array expression:

    WITH MyTable AS ( SELECT [STRUCT(['foo', 'bar'] AS items)] AS my_array )
    SELECT FLATTEN(my_array.items)
    FROM MyTable

    These data types have fields:

    • STRUCT
    • PROTO
    • JSON
  • array_element: If the field to access is an array field (array_field), you can additionally access a specific position in the field with the array subscript operator ([array_subscript_specifier]). This operation returns only elements at a selected position, rather than all elements, in the array field.

    For example, this query only returns values at position 0 in the items array field:

    WITH MyTable AS ( SELECT [STRUCT(['foo', 'bar'] AS items)] AS my_array )
    SELECT FLATTEN(my_array.items[OFFSET(0)])
    FROM MyTable

Details:

The array elements field access operation is not a typical expression that returns a typed value; it represents a concept outside the type system and can only be interpreted by the following operations:

  • FLATTEN operation: Returns an array. For example:

    FLATTEN(my_array.sales.prices)
  • UNNEST operation: Returns a table. array_expression must be a path expression. Implicitly implements the FLATTEN operator. For example, these do the same thing:

    UNNEST(my_array.sales.prices)
    UNNEST(FLATTEN(my_array.sales.prices))
  • FROM clause: Returns a table. array_expression must be a path expression. Implicitly implements the UNNEST operator and the FLATTEN operator. For example, these unnesting operations produce the same values for results:

    SELECT results FROM SalesTable, SalesTable.my_array.sales.prices AS results;
    SELECT results FROM SalesTable, UNNEST(my_array.sales.prices) AS results;
    SELECT results FROM SalesTable, UNNEST(FLATTEN(my_array.sales.prices)) AS results;

If NULL array elements are encountered, they are added to the resulting array.

Common shapes of this operation

This operation can take several shapes. The right-most value in the operation determines what type of array is returned. Here are some example shapes and a description of what they return:

The following shapes extract the final non-array field from each element of an array expression and return an array of those non-array field values.

  • array_expression.non_array_field_1
  • array_expression.non_array_field_1.array_field.non_array_field_2

The following shapes extract the final array field from each element of the array expression and concatenate the array fields together. An empty array or a NULL array contributes no elements to the resulting array.

  • array_expression.non_array_field_1.array_field_1
  • array_expression.non_array_field_1.array_field_1.non_array_field_2.array_field_2
  • array_expression.non_array_field_1.non_array_field_2.array_field_1

The following shapes extract the final array field from each element of the array expression at a specific position. Then they return an array of those extracted elements. An empty array or a NULL array contributes no elements to the resulting array.

  • array_expression.non_array_field_1.array_field_1[OFFSET(1)]
  • array_expression.non_array_field_1.array_field_1[SAFE_OFFSET(1)]
  • array_expression.non_array_field_1.non_array_field_2.array_field_1[ORDINAL(2)]
  • array_expression.non_array_field_1.non_array_field_2.array_field_1[SAFE_ORDINAL(2)]

Return Value

  • FLATTEN of an array element access operation returns an array.
  • UNNEST of an array element access operation, whether explicit or implicit, returns a table.

Examples

The next examples in this section reference a table called SalesTable, that contains a nested struct in an array called my_array:

WITH
  SalesTable AS (
    SELECT
      [
        STRUCT(
          [
            STRUCT([25.0, 75.0] AS prices),
            STRUCT([30.0] AS prices)
          ] AS sales
        )
      ] AS my_array
  )
SELECT * FROM SalesTable;

/*----------------------------------------------*
 | my_array                                     |
 +----------------------------------------------+
 | [{[{[25, 75] prices}, {[30] prices}] sales}] |
 *----------------------------------------------*/

This is what the array elements field access operator looks like in the FLATTEN operator:

SELECT FLATTEN(my_array.sales.prices) AS all_prices FROM SalesTable;

/*--------------*
 | all_prices   |
 +--------------+
 | [25, 75, 30] |
 *--------------*/

This is how you use the array subscript operator to only return values at a specific index in the prices array:

SELECT FLATTEN(my_array.sales.prices[OFFSET(0)]) AS first_prices FROM SalesTable;

/*--------------*
 | first_prices |
 +--------------+
 | [25, 30]     |
 *--------------*/

This is an example of an explicit UNNEST operation that includes the array elements field access operator:

SELECT all_prices FROM SalesTable, UNNEST(my_array.sales.prices) AS all_prices

/*------------*
 | all_prices |
 +------------+
 | 25         |
 | 75         |
 | 30         |
 *------------*/

This is an example of an implicit UNNEST operation that includes the array elements field access operator:

SELECT all_prices FROM SalesTable, SalesTable.my_array.sales.prices AS all_prices

/*------------*
 | all_prices |
 +------------+
 | 25         |
 | 75         |
 | 30         |
 *------------*/

This query produces an error because one of the prices arrays does not have an element at index 1 and OFFSET is used:

SELECT FLATTEN(my_array.sales.prices[OFFSET(1)]) AS second_prices FROM SalesTable;

-- Error

This query is like the previous query, but SAFE_OFFSET is used. This produces a NULL value instead of an error.

SELECT FLATTEN(my_array.sales.prices[SAFE_OFFSET(1)]) AS second_prices FROM SalesTable;

/*---------------*
 | second_prices |
 +---------------+
 | [75, NULL]    |
 *---------------*/

In this next example, an empty array and a NULL field value have been added to the query. These contribute no elements to the result.

WITH
  SalesTable AS (
    SELECT
      [
        STRUCT(
          [
            STRUCT([25.0, 75.0] AS prices),
            STRUCT([30.0] AS prices),
            STRUCT(ARRAY<DOUBLE>[] AS prices),
            STRUCT(NULL AS prices)
          ] AS sales
        )
      ] AS my_array
  )
SELECT FLATTEN(my_array.sales.prices) AS first_prices FROM SalesTable;

/*--------------*
 | first_prices |
 +--------------+
 | [25, 75, 30] |
 *--------------*/

The next examples in this section reference a protocol buffer called Album that looks like this:

message Album {
  optional string album_name = 1;
  repeated string song = 2;
  oneof group_name {
    string solo = 3;
    string duet = 4;
    string band = 5;
  }
}

Nested data is common in protocol buffers that have data within repeated messages. The following example extracts a flattened array of songs from a table called AlbumList that contains a column called Album of type PROTO.

WITH
  AlbumList AS (
    SELECT
      [
        NEW Album(
          'One Way' AS album_name,
          ['North', 'South'] AS song,
          'Crossroads' AS band),
        NEW Album(
          'After Hours' AS album_name,
          ['Snow', 'Ice', 'Water'] AS song,
          'Sunbirds' AS band)]
        AS albums_array
  )
SELECT FLATTEN(albums_array.song) AS songs FROM AlbumList

/*------------------------------*
 | songs                        |
 +------------------------------+
 | [North,South,Snow,Ice,Water] |
 *------------------------------*/

The following example extracts a flattened array of album names, one album name per row. The data comes from a table called AlbumList that contains a proto-typed column called Album.

WITH
  AlbumList AS (
    SELECT
      [
        (
          SELECT
            NEW Album(
              'One Way' AS album_name,
              ['North', 'South'] AS song,
              'Crossroads' AS band) AS album_col
        ),
        (
          SELECT
            NEW Album(
              'After Hours' AS album_name,
              ['Snow', 'Ice', 'Water'] AS song,
              'Sunbirds' AS band) AS album_col
        )]
        AS albums_array
  )
SELECT names FROM AlbumList, UNNEST(albums_array.album_name) AS names

/*----------------------*
 | names                |
 +----------------------+
 | One Way              |
 | After Hours          |
 *----------------------*/

Arithmetic operators

All arithmetic operators accept input of numeric type T, and the result type has type T unless otherwise indicated in the description below:

Name Syntax
Addition X + Y
Subtraction X - Y
Multiplication X * Y
Division X / Y
Unary Plus + X
Unary Minus - X

NOTE: Divide by zero operations return an error. To return a different result, consider the IEEE_DIVIDE or SAFE_DIVIDE functions.

Result types for Addition and Multiplication:

INPUTINT32INT64UINT32UINT64NUMERICBIGNUMERICFLOATDOUBLE
INT32INT64INT64INT64ERRORNUMERICBIGNUMERICDOUBLEDOUBLE
INT64INT64INT64INT64ERRORNUMERICBIGNUMERICDOUBLEDOUBLE
UINT32INT64INT64UINT64UINT64NUMERICBIGNUMERICDOUBLEDOUBLE
UINT64ERRORERRORUINT64UINT64NUMERICBIGNUMERICDOUBLEDOUBLE
NUMERICNUMERICNUMERICNUMERICNUMERICNUMERICBIGNUMERICDOUBLEDOUBLE
BIGNUMERICBIGNUMERICBIGNUMERICBIGNUMERICBIGNUMERICBIGNUMERICBIGNUMERICDOUBLEDOUBLE
FLOATDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLE
DOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLE

Result types for Subtraction:

INPUTINT32INT64UINT32UINT64NUMERICBIGNUMERICFLOATDOUBLE
INT32INT64INT64INT64ERRORNUMERICBIGNUMERICDOUBLEDOUBLE
INT64INT64INT64INT64ERRORNUMERICBIGNUMERICDOUBLEDOUBLE
UINT32INT64INT64INT64INT64NUMERICBIGNUMERICDOUBLEDOUBLE
UINT64ERRORERRORINT64INT64NUMERICBIGNUMERICDOUBLEDOUBLE
NUMERICNUMERICNUMERICNUMERICNUMERICNUMERICBIGNUMERICDOUBLEDOUBLE
BIGNUMERICBIGNUMERICBIGNUMERICBIGNUMERICBIGNUMERICBIGNUMERICBIGNUMERICDOUBLEDOUBLE
FLOATDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLE
DOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLE

Result types for Division:

INPUTINT32INT64UINT32UINT64NUMERICBIGNUMERICFLOATDOUBLE
INT32DOUBLEDOUBLEDOUBLEDOUBLENUMERICBIGNUMERICDOUBLEDOUBLE
INT64DOUBLEDOUBLEDOUBLEDOUBLENUMERICBIGNUMERICDOUBLEDOUBLE
UINT32DOUBLEDOUBLEDOUBLEDOUBLENUMERICBIGNUMERICDOUBLEDOUBLE
UINT64DOUBLEDOUBLEDOUBLEDOUBLENUMERICBIGNUMERICDOUBLEDOUBLE
NUMERICNUMERICNUMERICNUMERICNUMERICNUMERICBIGNUMERICDOUBLEDOUBLE
BIGNUMERICBIGNUMERICBIGNUMERICBIGNUMERICBIGNUMERICBIGNUMERICBIGNUMERICDOUBLEDOUBLE
FLOATDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLE
DOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLE

Result types for Unary Plus:

INPUTINT32INT64UINT32UINT64NUMERICBIGNUMERICFLOATDOUBLE
OUTPUTINT32INT64UINT32UINT64NUMERICBIGNUMERICFLOATDOUBLE

Result types for Unary Minus:

INPUTINT32INT64UINT32UINT64NUMERICBIGNUMERICFLOATDOUBLE
OUTPUTINT32INT64ERRORERRORNUMERICBIGNUMERICFLOATDOUBLE

Date arithmetics operators

Operators '+' and '-' can be used for arithmetic operations on dates.

date_expression + int64_expression
int64_expression + date_expression
date_expression - int64_expression

Description

Adds or subtracts int64_expression days to or from date_expression. This is equivalent to DATE_ADD or DATE_SUB functions, when interval is expressed in days.

Return Data Type

DATE

Example

SELECT DATE "2020-09-22" + 1 AS day_later, DATE "2020-09-22" - 7 AS week_ago

/*------------+------------*
 | day_later  | week_ago   |
 +------------+------------+
 | 2020-09-23 | 2020-09-15 |
 *------------+------------*/

Datetime subtraction

date_expression - date_expression
timestamp_expression - timestamp_expression
datetime_expression - datetime_expression

Description

Computes the difference between two datetime values as an interval.

Return Data Type

INTERVAL

Example

SELECT
  DATE "2021-05-20" - DATE "2020-04-19" AS date_diff,
  TIMESTAMP "2021-06-01 12:34:56.789" - TIMESTAMP "2021-05-31 00:00:00" AS time_diff

/*-------------------+------------------------*
 | date_diff         | time_diff              |
 +-------------------+------------------------+
 | 0-0 396 0:0:0     | 0-0 0 36:34:56.789     |
 *-------------------+------------------------*/

Interval arithmetic operators

Addition and subtraction

date_expression + interval_expression = DATETIME
date_expression - interval_expression = DATETIME
timestamp_expression + interval_expression = TIMESTAMP
timestamp_expression - interval_expression = TIMESTAMP
datetime_expression + interval_expression = DATETIME
datetime_expression - interval_expression = DATETIME

Description

Adds an interval to a datetime value or subtracts an interval from a datetime value.

Example

SELECT
  DATE "2021-04-20" + INTERVAL 25 HOUR AS date_plus,
  TIMESTAMP "2021-05-02 00:01:02.345" - INTERVAL 10 SECOND AS time_minus;

/*-------------------------+--------------------------------*
 | date_plus               | time_minus                     |
 +-------------------------+--------------------------------+
 | 2021-04-21 01:00:00     | 2021-05-02 00:00:52.345+00     |
 *-------------------------+--------------------------------*/

Multiplication and division

interval_expression * integer_expression = INTERVAL
interval_expression / integer_expression = INTERVAL

Description

Multiplies or divides an interval value by an integer.

Example

SELECT
  INTERVAL '1:2:3' HOUR TO SECOND * 10 AS mul1,
  INTERVAL 35 SECOND * 4 AS mul2,
  INTERVAL 10 YEAR / 3 AS div1,
  INTERVAL 1 MONTH / 12 AS div2

/*----------------+--------------+-------------+--------------*
 | mul1           | mul2         | div1        | div2         |
 +----------------+--------------+-------------+--------------+
 | 0-0 0 10:20:30 | 0-0 0 0:2:20 | 3-4 0 0:0:0 | 0-0 2 12:0:0 |
 *----------------+--------------+-------------+--------------*/

Bitwise operators

All bitwise operators return the same type and the same length as the first operand.

Name Syntax Input Data Type Description
Bitwise not ~ X Integer or BYTES Performs logical negation on each bit, forming the ones' complement of the given binary value.
Bitwise or X | Y X: Integer or BYTES
Y: Same type as X
Takes two bit patterns of equal length and performs the logical inclusive OR operation on each pair of the corresponding bits.

This operator throws an error if X and Y are bytes of different lengths.

Bitwise xor X ^ Y X: Integer or BYTES
Y: Same type as X
Takes two bit patterns of equal length and performs the logical exclusive OR operation on each pair of the corresponding bits.

This operator throws an error if X and Y are bytes of different lengths.

Bitwise and X & Y X: Integer or BYTES
Y: Same type as X
Takes two bit patterns of equal length and performs the logical AND operation on each pair of the corresponding bits.

This operator throws an error if X and Y are bytes of different lengths.

Left shift X << Y X: Integer or BYTES
Y: INT64
Shifts the first operand X to the left. This operator returns 0 or a byte sequence of b'\x00' if the second operand Y is greater than or equal to

the bit length of the first operand X (for example, 64 if X has the type INT64).

This operator throws an error if Y is negative.

Right shift X >> Y X: Integer or BYTES
Y: INT64
Shifts the first operand X to the right. This operator does not do sign bit extension with a signed type (i.e., it fills vacant bits on the left with 0). This operator returns 0 or a byte sequence of b'\x00' if the second operand Y is greater than or equal to

the bit length of the first operand X (for example, 64 if X has the type INT64).

This operator throws an error if Y is negative.

Logical operators

ZetaSQL supports the AND, OR, and NOT logical operators. Logical operators allow only BOOL or NULL input and use three-valued logic to produce a result. The result can be TRUE, FALSE, or NULL:

x y x AND y x OR y
TRUE TRUE TRUE TRUE
TRUE FALSE FALSE TRUE
TRUE NULL NULL TRUE
FALSE TRUE FALSE TRUE
FALSE FALSE FALSE FALSE
FALSE NULL FALSE NULL
NULL TRUE NULL TRUE
NULL FALSE FALSE NULL
NULL NULL NULL NULL
x NOT x
TRUE FALSE
FALSE TRUE
NULL NULL

Examples

The examples in this section reference a table called entry_table:

/*-------*
 | entry |
 +-------+
 | a     |
 | b     |
 | c     |
 | NULL  |
 *-------*/
SELECT 'a' FROM entry_table WHERE entry = 'a'

-- a => 'a' = 'a' => TRUE
-- b => 'b' = 'a' => FALSE
-- NULL => NULL = 'a' => NULL

/*-------*
 | entry |
 +-------+
 | a     |
 *-------*/
SELECT entry FROM entry_table WHERE NOT (entry = 'a')

-- a => NOT('a' = 'a') => NOT(TRUE) => FALSE
-- b => NOT('b' = 'a') => NOT(FALSE) => TRUE
-- NULL => NOT(NULL = 'a') => NOT(NULL) => NULL

/*-------*
 | entry |
 +-------+
 | b     |
 | c     |
 *-------*/
SELECT entry FROM entry_table WHERE entry IS NULL

-- a => 'a' IS NULL => FALSE
-- b => 'b' IS NULL => FALSE
-- NULL => NULL IS NULL => TRUE

/*-------*
 | entry |
 +-------+
 | NULL  |
 *-------*/

Graph concatenation operator

graph_path || graph_path [ || ... ]

Description

Combines multiple graph paths into one and preserves the original order of the nodes and edges.

Arguments:

  • graph_path: A GRAPH_PATH value that represents a graph path to concatenate.

Details

This operator produces an error if the last node in the first path isn't the same as the first node in the second path.

-- This successfully produces the concatenated path called `full_path`.
MATCH
  p=(src:Account)-[t1:Transfers]->(mid:Account),
  q=(mid)-[t2:Transfers]->(dst:Account)
LET full_path = p || q
-- This produces an error because the first node of the path to be concatenated
-- (mid2) is not equal to the last node of the previous path (mid1).
MATCH
  p=(src:Account)-[t1:Transfers]->(mid1:Account),
  q=(mid2:Account)-[t2:Transfers]->(dst:Account)
LET full_path = p || q

The first node in each subsequent path is removed from the concatenated path.

-- The concatenated path called `full_path` contains these elements:
-- src, t1, mid, t2, dst.
MATCH
  p=(src:Account)-[t1:Transfers]->(mid:Account),
  q=(mid)-[t2:Transfers]->(dst:Account)
LET full_path = p || q

If any graph_path is NULL, produces NULL.

Example

In the following query, a path called p and q are concatenated. Notice that mid is used at the end of the first path and at the beginning of the second path. Also notice that the duplicate mid is removed from the concatenated path called full_path:

GRAPH FinGraph
MATCH
  p=(src:Account)-[t1:Transfers]->(mid:Account),
  q = (mid)-[t2:Transfers]->(dst:Account)
LET full_path = p || q
RETURN
  JSON_QUERY(TO_JSON(full_path)[0], '$.labels') AS element_a,
  JSON_QUERY(TO_JSON(full_path)[1], '$.labels') AS element_b,
  JSON_QUERY(TO_JSON(full_path)[2], '$.labels') AS element_c,
  JSON_QUERY(TO_JSON(full_path)[3], '$.labels') AS element_d,
  JSON_QUERY(TO_JSON(full_path)[4], '$.labels') AS element_e,
  JSON_QUERY(TO_JSON(full_path)[5], '$.labels') AS element_f

/*-------------------------------------------------------------------------------------*
 | element_a   | element_b     | element_c   | element_d     | element_e   | element_f |
 +-------------------------------------------------------------------------------------+
 | ["Account"] | ["Transfers"] | ["Account"] | ["Transfers"] | ["Account"] |           |
 | ...         | ...           | ...         | ...           | ...         | ...       |
 *-------------------------------------------------------------------------------------/*

The following query produces an error because the last node for p must be the first node for q:

-- Error: `mid1` and `mid2` are not equal.
GRAPH FinGraph
MATCH
  p=(src:Account)-[t1:Transfers]->(mid1:Account),
  q=(mid2:Account)-[t2:Transfers]->(dst:Account)
LET full_path = p || q
RETURN TO_JSON(full_path) AS results

The following query produces an error because the path called p is NULL:

-- Error: a graph path is NULL.
GRAPH FinGraph
MATCH
  p=NULL,
  q=(mid:Account)-[t2:Transfers]->(dst:Account)
LET full_path = p || q
RETURN TO_JSON(full_path) AS results

Graph logical operators

ZetaSQL supports the following logical operators in element pattern label expressions:

Name Syntax Description
NOT !X Returns TRUE if X is not included, otherwise, returns FALSE.
OR X | Y Returns TRUE if either X or Y is included, otherwise, returns FALSE.
AND X & Y Returns TRUE if both X and Y are included, otherwise, returns FALSE.

Graph predicates

ZetaSQL supports the following graph-specific predicates in graph expressions. A predicate can produce TRUE, FALSE, or NULL.

IS DESTINATION predicate

node IS [ NOT ] DESTINATION [ OF ] edge

Description

In a graph, checks to see if a node is or isn't the destination of an edge. Can produce TRUE, FALSE, or NULL.

Arguments:

  • node: The graph pattern variable for the node element.
  • edge: The graph pattern variable for the edge element.

Examples

GRAPH FinGraph
MATCH (a:Account)-[transfer:Transfers]-(b:Account)
WHERE a IS DESTINATION of transfer
RETURN a.id AS a_id, b.id AS b_id

/*-------------+
 | a_id | b_id |
 +-------------+
 | 16   | 7    |
 | 16   | 7    |
 | 20   | 16   |
 | 7    | 20   |
 | 16   | 20   |
 +-------------*/
GRAPH FinGraph
MATCH (a:Account)-[transfer:Transfers]-(b:Account)
WHERE b IS DESTINATION of transfer
RETURN a.id AS a_id, b.id AS b_id

/*-------------+
 | a_id | b_id |
 +-------------+
 | 7    | 16   |
 | 7    | 16   |
 | 16   | 20   |
 | 20   | 7    |
 | 20   | 16   |
 +-------------*/

IS SOURCE predicate

node IS [ NOT ] SOURCE [ OF ] edge

Description

In a graph, checks to see if a node is or isn't the source of an edge. Can produce TRUE, FALSE, or NULL.

Arguments:

  • node: The graph pattern variable for the node element.
  • edge: The graph pattern variable for the edge element.

Examples

GRAPH FinGraph
MATCH (a:Account)-[transfer:Transfers]-(b:Account)
WHERE a IS SOURCE of transfer
RETURN a.id AS a_id, b.id AS b_id

/*-------------+
 | a_id | b_id |
 +-------------+
 | 20   | 7    |
 | 7    | 16   |
 | 7    | 16   |
 | 20   | 16   |
 | 16   | 20   |
 +-------------*/
GRAPH FinGraph
MATCH (a:Account)-[transfer:Transfers]-(b:Account)
WHERE b IS SOURCE of transfer
RETURN a.id AS a_id, b.id AS b_id

/*-------------+
 | a_id | b_id |
 +-------------+
 | 7    | 20   |
 | 16   | 7    |
 | 16   | 7    |
 | 16   | 20   |
 | 20   | 16   |
 +-------------*/

PROPERTY_EXISTS predicate

PROPERTY_EXISTS(element, element_property)

Description

In a graph, checks to see if a property exists for an element. Can produce TRUE, FALSE, or NULL.

Arguments:

  • element: The graph pattern variable for a node or edge element.
  • element_property: The name of the property to look for in element. The property name must refer to a property in the graph. If the property does not exist in the graph, an error is produced. The property name is resolved in a case-insensitive manner.

Example

GRAPH FinGraph
MATCH (n:Person|Account WHERE PROPERTY_EXISTS(n, name))
RETURN n.name

/*------+
 | name |
 +------+
 | Alex |
 | Dana |
 | Lee  |
 +------*/

SAME predicate

SAME (element, element[, element])

Description

In a graph, determines if all graph elements in a list bind to the same node or edge. Can produce TRUE, FALSE, or NULL.

Arguments:

  • element: The graph pattern variable for a node or edge element.

Example

The following query checks to see if a and b are not the same person.

GRAPH FinGraph
MATCH (src:Account)<-[transfer:Transfers]-(dest:Account)
WHERE NOT SAME(src, dest)
RETURN src.id AS source_id, dest.id AS destination_id

/*----------------------------+
 | source_id | destination_id |
 +----------------------------+
 | 7         | 20             |
 | 16        | 7              |
 | 16        | 7              |
 | 16        | 20             |
 | 20        | 16             |
 +----------------------------*/

Comparison operators

Compares operands and produces the results of the comparison as a BOOL value. These comparison operators are available:

Name Syntax Description
Less Than X < Y Returns TRUE if X is less than Y.

This operator supports specifying collation.

  </td>
</tr>
<tr>
  <td>Less Than or Equal To</td>
  <td><code>X &lt;= Y</code></td>
  <td>
    Returns <code>TRUE</code> if <code>X</code> is less than or equal to
    <code>Y</code>.

This operator supports specifying collation.

  </td>
</tr>
<tr>
  <td>Greater Than</td>
  <td><code>X &gt; Y</code></td>
  <td>
    Returns <code>TRUE</code> if <code>X</code> is greater than
    <code>Y</code>.

This operator supports specifying collation.

  </td>
</tr>
<tr>
  <td>Greater Than or Equal To</td>
  <td><code>X &gt;= Y</code></td>
  <td>
    Returns <code>TRUE</code> if <code>X</code> is greater than or equal to
    <code>Y</code>.

This operator supports specifying collation.

  </td>
</tr>
<tr>
  <td>Equal</td>
  <td><code>X = Y</code></td>
  <td>
    Returns <code>TRUE</code> if <code>X</code> is equal to <code>Y</code>.

This operator supports specifying collation.

  </td>
</tr>
<tr>
  <td>Not Equal</td>
  <td><code>X != Y</code><br><code>X &lt;&gt; Y</code></td>
  <td>
    Returns <code>TRUE</code> if <code>X</code> is not equal to
    <code>Y</code>.

This operator supports specifying collation.

  </td>
</tr>
<tr>
  <td><code>BETWEEN</code></td>
  <td><code>X [NOT] BETWEEN Y AND Z</code></td>
  <td>
    <p>
      Returns <code>TRUE</code> if <code>X</code> is [not] within the range
      specified. The result of <code>X BETWEEN Y AND Z</code> is equivalent
      to <code>Y &lt;= X AND X &lt;= Z</code> but <code>X</code> is
      evaluated only once in the former.

This operator supports specifying collation.

    </p>
  </td>
</tr>
<tr>
  <td><code>LIKE</code></td>
  <td><code>X [NOT] LIKE Y</code></td>
  <td>
    See the <a href="#like_operator">`LIKE` operator</a>

    for details.
  </td>
</tr>
<tr>
  <td><code>IN</code></td>
  <td>Multiple</td>
  <td>
    See the <a href="#in_operator">`IN` operator</a>

    for details.
  </td>
</tr>

The following rules apply to operands in a comparison operator:

  • The operands must be comparable.
  • A comparison operator generally requires both operands to be of the same type.
  • If the operands are of different types, and the values of those types can be converted to a common type without loss of precision, they are generally coerced to that common type for the comparison.
  • A literal operand is generally coerced to the same data type of a non-literal operand that is part of the comparison.
  • Comparisons between operands that are signed and unsigned integers is allowed.
  • Struct operands support only these comparison operators: equal (=), not equal (!= and <>), and IN.

The following rules apply when comparing these data types:

  • Floating point: All comparisons with NaN return FALSE, except for != and <>, which return TRUE.

  • BOOL: FALSE is less than TRUE.

  • STRING: Strings are compared codepoint-by-codepoint, which means that canonically equivalent strings are only guaranteed to compare as equal if they have been normalized first.

  • JSON: You can't compare JSON, but you can compare the values inside of JSON if you convert the values to SQL values first. For more information, see JSON functions.

  • NULL: Any operation with a NULL input returns NULL.

  • STRUCT: When testing a struct for equality, it's possible that one or more fields are NULL. In such cases:

    • If all non-NULL field values are equal, the comparison returns NULL.
    • If any non-NULL field values are not equal, the comparison returns FALSE.

    The following table demonstrates how STRUCT data types are compared when they have fields that are NULL valued.

    Struct1 Struct2 Struct1 = Struct2
    STRUCT(1, NULL) STRUCT(1, NULL) NULL
    STRUCT(1, NULL) STRUCT(2, NULL) FALSE
    STRUCT(1,2) STRUCT(1, NULL) NULL

EXISTS operator

EXISTS ( subquery )

Description

Returns TRUE if the subquery produces one or more rows. Returns FALSE if the subquery produces zero rows. Never returns NULL. To learn more about how you can use a subquery with EXISTS, see EXISTS subqueries.

Examples

In this example, the EXISTS operator returns FALSE because there are no rows in Words where the direction is south:

WITH Words AS (
  SELECT 'Intend' as value, 'east' as direction UNION ALL
  SELECT 'Secure', 'north' UNION ALL
  SELECT 'Clarity', 'west'
 )
SELECT EXISTS ( SELECT value FROM Words WHERE direction = 'south' ) as result;

/*--------*
 | result |
 +--------+
 | FALSE  |
 *--------*/

IN operator

The IN operator supports the following syntax:

search_value [NOT] IN value_set

value_set:
  {
    (expression[, ...])
    | (subquery)
    | UNNEST(array_expression)
  }

Description

Checks for an equal value in a set of values. Semantic rules apply, but in general, IN returns TRUE if an equal value is found, FALSE if an equal value is excluded, otherwise NULL. NOT IN returns FALSE if an equal value is found, TRUE if an equal value is excluded, otherwise NULL.

  • search_value: The expression that is compared to a set of values.
  • value_set: One or more values to compare to a search value.
    • (expression[, ...]): A list of expressions.

    • (subquery): A subquery that returns a single column. The values in that column are the set of values. If no rows are produced, the set of values is empty.

    • UNNEST(array_expression): An UNNEST operator that returns a column of values from an array expression. This is equivalent to:

      IN (SELECT element FROM UNNEST(array_expression) AS element)

This operator supports collation, but these limitations apply:

  • [NOT] IN UNNEST does not support collation.
  • If collation is used with a list of expressions, there must be at least one item in the list.

Semantic rules

When using the IN operator, the following semantics apply in this order:

  • Returns FALSE if value_set is empty.
  • Returns NULL if search_value is NULL.
  • Returns TRUE if value_set contains a value equal to search_value.
  • Returns NULL if value_set contains a NULL.
  • Returns FALSE.

When using the NOT IN operator, the following semantics apply in this order:

  • Returns TRUE if value_set is empty.
  • Returns NULL if search_value is NULL.
  • Returns FALSE if value_set contains a value equal to search_value.
  • Returns NULL if value_set contains a NULL.
  • Returns TRUE.

The semantics of:

x IN (y, z, ...)

are defined as equivalent to:

(x = y) OR (x = z) OR ...

and the subquery and array forms are defined similarly.

x NOT IN ...

is equivalent to:

NOT(x IN ...)

The UNNEST form treats an array scan like UNNEST in the FROM clause:

x [NOT] IN UNNEST(<array expression>)

This form is often used with array parameters. For example:

x IN UNNEST(@array_parameter)

See the Arrays topic for more information on how to use this syntax.

IN can be used with multi-part keys by using the struct constructor syntax. For example:

(Key1, Key2) IN ( (12,34), (56,78) )
(Key1, Key2) IN ( SELECT (table.a, table.b) FROM table )

See the Struct Type topic for more information.

Return Data Type

BOOL

Examples

You can use these WITH clauses to emulate temporary tables for Words and Items in the following examples:

WITH Words AS (
  SELECT 'Intend' as value UNION ALL
  SELECT 'Secure' UNION ALL
  SELECT 'Clarity' UNION ALL
  SELECT 'Peace' UNION ALL
  SELECT 'Intend'
 )
SELECT * FROM Words;

/*----------*
 | value    |
 +----------+
 | Intend   |
 | Secure   |
 | Clarity  |
 | Peace    |
 | Intend   |
 *----------*/
WITH
  Items AS (
    SELECT STRUCT('blue' AS color, 'round' AS shape) AS info UNION ALL
    SELECT STRUCT('blue', 'square') UNION ALL
    SELECT STRUCT('red', 'round')
  )
SELECT * FROM Items;

/*----------------------------*
 | info                       |
 +----------------------------+
 | {blue color, round shape}  |
 | {blue color, square shape} |
 | {red color, round shape}   |
 *----------------------------*/

Example with IN and an expression:

SELECT * FROM Words WHERE value IN ('Intend', 'Secure');

/*----------*
 | value    |
 +----------+
 | Intend   |
 | Secure   |
 | Intend   |
 *----------*/

Example with NOT IN and an expression:

SELECT * FROM Words WHERE value NOT IN ('Intend');

/*----------*
 | value    |
 +----------+
 | Secure   |
 | Clarity  |
 | Peace    |
 *----------*/

Example with IN, a scalar subquery, and an expression:

SELECT * FROM Words WHERE value IN ((SELECT 'Intend'), 'Clarity');

/*----------*
 | value    |
 +----------+
 | Intend   |
 | Clarity  |
 | Intend   |
 *----------*/

Example with IN and an UNNEST operation:

SELECT * FROM Words WHERE value IN UNNEST(['Secure', 'Clarity']);

/*----------*
 | value    |
 +----------+
 | Secure   |
 | Clarity  |
 *----------*/

Example with IN and a struct:

SELECT
  (SELECT AS STRUCT Items.info) as item
FROM
  Items
WHERE (info.shape, info.color) IN (('round', 'blue'));

/*------------------------------------*
 | item                               |
 +------------------------------------+
 | { {blue color, round shape} info } |
 *------------------------------------*/

IS operators

IS operators return TRUE or FALSE for the condition they are testing. They never return NULL, even for NULL inputs, unlike the IS_INF and IS_NAN functions defined in Mathematical Functions. If NOT is present, the output BOOL value is inverted.

Function Syntax Input Data Type Result Data Type Description
X IS TRUE BOOL BOOL Evaluates to TRUE if X evaluates to TRUE. Otherwise, evaluates to FALSE.
X IS NOT TRUE BOOL BOOL Evaluates to FALSE if X evaluates to TRUE. Otherwise, evaluates to TRUE.
X IS FALSE BOOL BOOL Evaluates to TRUE if X evaluates to FALSE. Otherwise, evaluates to FALSE.
X IS NOT FALSE BOOL BOOL Evaluates to FALSE if X evaluates to FALSE. Otherwise, evaluates to TRUE.
X IS NULL Any value type BOOL Evaluates to TRUE if X evaluates to NULL. Otherwise evaluates to FALSE.
X IS NOT NULL Any value type BOOL Evaluates to FALSE if X evaluates to NULL. Otherwise evaluates to TRUE.
X IS UNKNOWN BOOL BOOL Evaluates to TRUE if X evaluates to NULL. Otherwise evaluates to FALSE.
X IS NOT UNKNOWN BOOL BOOL Evaluates to FALSE if X evaluates to NULL. Otherwise, evaluates to TRUE.

IS DISTINCT FROM operator

expression_1 IS [NOT] DISTINCT FROM expression_2

Description

IS DISTINCT FROM returns TRUE if the input values are considered to be distinct from each other by the DISTINCT and GROUP BY clauses. Otherwise, returns FALSE.

a IS DISTINCT FROM b being TRUE is equivalent to:

  • SELECT COUNT(DISTINCT x) FROM UNNEST([a,b]) x returning 2.
  • SELECT * FROM UNNEST([a,b]) x GROUP BY x returning 2 rows.

a IS DISTINCT FROM b is equivalent to NOT (a = b), except for the following cases:

  • This operator never returns NULL so NULL values are considered to be distinct from non-NULL values, not other NULL values.
  • NaN values are considered to be distinct from non-NaN values, but not other NaN values.

Input values:

  • expression_1: The first value to compare. This can be a groupable data type, NULL or NaN.
  • expression_2: The second value to compare. This can be a groupable data type, NULL or NaN.
  • NOT: If present, the output BOOL value is inverted.

Return type

BOOL

Examples

These return TRUE:

SELECT 1 IS DISTINCT FROM 2
SELECT 1 IS DISTINCT FROM NULL
SELECT 1 IS NOT DISTINCT FROM 1
SELECT NULL IS NOT DISTINCT FROM NULL

These return FALSE:

SELECT NULL IS DISTINCT FROM NULL
SELECT 1 IS DISTINCT FROM 1
SELECT 1 IS NOT DISTINCT FROM 2
SELECT 1 IS NOT DISTINCT FROM NULL

LIKE operator

expression_1 [NOT] LIKE expression_2

Description

LIKE returns TRUE if the string in the first operand expression_1 matches a pattern specified by the second operand expression_2, otherwise returns FALSE.

NOT LIKE returns TRUE if the string in the first operand expression_1 does not match a pattern specified by the second operand expression_2, otherwise returns FALSE.

Expressions can contain these characters:

  • A percent sign (%) matches any number of characters or bytes.
  • An underscore (_) matches a single character or byte.
  • You can escape \ , _, or % using two backslashes. For example, \\% . If you are using raw strings, only a single backslash is required. For example, r'\%'.

This operator supports collation, but caveats apply:

  • Each % character in expression_2 represents an arbitrary string specifier. An arbitrary string specifier can represent any sequence of 0 or more characters.

  • A character in the expression represents itself and is considered a single character specifier unless:

    • The character is a percent sign (%).

    • The character is an underscore (_) and the collator is not und:ci.

  • These additional rules apply to the underscore (_) character:

    • If the collator is not und:ci, an error is produced when an underscore is not escaped in expression_2.

    • If the collator is not und:ci, the underscore is not allowed when the operands have collation specified.

    • Some compatibility composites, such as the fi-ligature () and the telephone sign (), will produce a match if they are compared to an underscore.

    • A single underscore matches the idea of what a character is, based on an approximation known as a grapheme cluster.

  • For a contiguous sequence of single character specifiers, equality depends on the collator and its language tags and tailoring.

    • By default, the und:ci collator does not fully normalize a string. Some canonically equivalent strings are considered unequal for both the = and LIKE operators.

    • The LIKE operator with collation has the same behavior as the = operator when there are no wildcards in the strings.

    • Character sequences with secondary or higher-weighted differences are considered unequal. This includes accent differences and some special cases.

      For example there are three ways to produce German sharp ß:

      • \u1E9E
      • \U00DF
      • ss

      \u1E9E and \U00DF are considered equal but differ in tertiary. They are considered equal with und:ci collation but different from ss, which has secondary differences.

    • Character sequences with tertiary or lower-weighted differences are considered equal. This includes case differences and kana subtype differences, which are considered equal.

  • There are ignorable characters defined in Unicode. Ignorable characters are ignored in the pattern matching.

Return type

BOOL

Examples

The following examples illustrate how you can check to see if the string in the first operand matches a pattern specified by the second operand.

-- Returns TRUE
SELECT 'apple' LIKE 'a%';
-- Returns FALSE
SELECT '%a' LIKE 'apple';
-- Returns FALSE
SELECT 'apple' NOT LIKE 'a%';
-- Returns TRUE
SELECT '%a' NOT LIKE 'apple';
-- Produces an error
SELECT NULL LIKE 'a%';
-- Produces an error
SELECT 'apple' LIKE NULL;

The following example illustrates how to search multiple patterns in an array to find a match with the LIKE operator:

WITH Words AS
 (SELECT 'Intend with clarity.' as value UNION ALL
  SELECT 'Secure with intention.' UNION ALL
  SELECT 'Clarity and security.')
SELECT value
FROM Words
WHERE ARRAY_INCLUDES(['%ity%', '%and%'], pattern->(Words.value LIKE pattern));

/*------------------------+
 | value                  |
 +------------------------+
 | Intend with clarity.   |
 | Clarity and security.  |
 +------------------------*/

The following examples illustrate how collation can be used with the LIKE operator.

-- Returns FALSE
'Foo' LIKE '%foo%'
-- Returns TRUE
COLLATE('Foo', 'und:ci') LIKE COLLATE('%foo%', 'und:ci');
-- Returns TRUE
COLLATE('Foo', 'und:ci') = COLLATE('foo', 'und:ci');
-- Produces an error
COLLATE('Foo', 'und:ci') LIKE COLLATE('%foo%', 'binary');
-- Produces an error
COLLATE('Foo', 'und:ci') LIKE COLLATE('%f_o%', 'und:ci');
-- Returns TRUE
COLLATE('Foo_', 'und:ci') LIKE COLLATE('%foo\\_%', 'und:ci');

There are two capital forms of ß. We can use either SS or as upper case. While the difference between ß and is case difference (tertiary difference), the difference between sharp s and ss is secondary and considered not equal using the und:ci collator. For example:

-- Returns FALSE
'MASSE' LIKE 'Maße';
-- Returns FALSE
COLLATE('MASSE', 'und:ci') LIKE '%Maße%';
-- Returns FALSE
COLLATE('MASSE', 'und:ci') = COLLATE('Maße', 'und:ci');

The kana differences in Japanese are considered as tertiary or quaternary differences, and should be considered as equal in the und:ci collator with secondary strength.

  • '\u3042' is 'あ' (hiragana)
  • '\u30A2' is 'ア' (katakana)

For example:

-- Returns FALSE
'\u3042' LIKE '%\u30A2%';
-- Returns TRUE
COLLATE('\u3042', 'und:ci') LIKE COLLATE('%\u30A2%', 'und:ci');
-- Returns TRUE
COLLATE('\u3042', 'und:ci') = COLLATE('\u30A2', 'und:ci');

When comparing two strings, the und:ci collator compares the collation units based on the specification of the collation. Even though the number of code points is different, the two strings are considered equal when the collation units are considered the same.

  • '\u0041\u030A' is 'Å' (two code points)
  • '\u0061\u030A' is 'å' (two code points)
  • '\u00C5' is 'Å' (one code point)

In the following examples, the difference between '\u0061\u030A' and '\u00C5' is tertiary.

-- Returns FALSE
'\u0061\u030A' LIKE '%\u00C5%';
-- Returns TRUE
COLLATE('\u0061\u030A', 'und:ci') LIKE '%\u00C5%';
-- Returns TRUE
COLLATE('\u0061\u030A', 'und:ci') = COLLATE('\u00C5', 'und:ci');

In the following example, '\u0083' is a NO BREAK HERE character and is ignored.

-- Returns FALSE
'\u0083' LIKE '';
-- Returns TRUE
COLLATE('\u0083', 'und:ci') LIKE '';

Quantified LIKE operator

The quantified LIKE operator supports the following syntax:

search_value [NOT] LIKE quantifier patterns

quantifier:
 { ANY | SOME | ALL }

patterns:
  {
    pattern_expression_list
    | pattern_subquery
    | pattern_array
  }

pattern_expression_list:
  (expression[, ...])

pattern_subquery:
  (subquery)

pattern_array:
  UNNEST(array_expression)

Description

Checks search_value for matches against several patterns. Each comparison is case-sensitive. Wildcard searches are supported. Semantic rules apply, but in general, LIKE returns TRUE if a matching pattern is found, FALSE if a matching pattern is not found, or otherwise NULL. NOT LIKE returns FALSE if a matching pattern is found, TRUE if a matching pattern is not found, or otherwise NULL.

  • search_value: The value to search for matching patterns. This value can be a STRING or BYTES type.

  • patterns: The patterns to look for in the search value. Each pattern must resolve to the same type as search_value.

    • pattern_expression_list: A list of one or more patterns that match the search_value type.

    • pattern_subquery: A subquery that returns a single column with the same type as search_value.

    • pattern_array: An UNNEST operation that returns a column of values with the same type as search_value from an array expression.

    The regular expressions that are supported by the LIKE operator are also supported by patterns in the quantified LIKE operator.

  • quantifier: Condition for pattern matching.

    • ANY: Checks if the set of patterns contains at least one pattern that matches the search value.

    • SOME: Synonym for ANY.

    • ALL: Checks if every pattern in the set of patterns matches the search value.

Collation caveats

Collation is supported, but with the following caveats:

  • The collation caveats that apply to the LIKE operator also apply to the quantified LIKE operator.
  • If a collation-supported input contains no collation specification or an empty collation specification and another input contains an explicitly defined collation, the explicitly defined collation is used for all of the inputs.
  • All inputs with a non-empty, explicitly defined collation specification must have the same type of collation specification, otherwise an error is thrown.

Semantics rules

When using the quantified LIKE operator with ANY or SOME, the following semantics apply in this order:

  • Returns FALSE if patterns is empty.
  • Returns NULL if search_value is NULL.
  • Returns TRUE if search_value matches at least one value in patterns.
  • Returns NULL if a pattern in patterns is NULL and other patterns in patterns don't match.
  • Returns FALSE.

When using the quantified LIKE operator with ALL, the following semantics apply in this order:

  • For pattern_subquery, returns TRUE if patterns is empty.
  • For pattern_array, returns FALSE if patterns is empty.
  • Returns NULL if search_value is NULL.
  • Returns TRUE if search_value matches all values in patterns.
  • Returns NULL if a pattern in patterns is NULL and other patterns in patterns don't match.
  • Returns FALSE.

When using the quantified NOT LIKE operator with ANY or SOME, the following semantics apply in this order:

  • For pattern_subquery, returns TRUE if patterns is empty.
  • For pattern_array, returns TRUE if patterns is empty.
  • Returns NULL if search_value is NULL.
  • Returns TRUE if search_value doesn't match at least one value in patterns.
  • Returns NULL if a pattern in patterns is NULL and other patterns in patterns don't match.
  • Returns FALSE.

When using the quantified NOT LIKE operator with ALL, the following semantics apply in this order:

  • For pattern_subquery, returns FALSE if patterns is empty.
  • For pattern_array, returns TRUE if patterns is empty.
  • Returns NULL if search_value is NULL.
  • Returns TRUE if search_value matches none of the values in patterns.
  • Returns NULL if a pattern in patterns is NULL and other patterns in patterns don't match.
  • Returns FALSE.

Return Data Type

BOOL

Examples

The following example checks to see if the Intend% or %intention% pattern exists in a value and produces that value if either pattern is found:

WITH Words AS
 (SELECT 'Intend with clarity.' as value UNION ALL
  SELECT 'Secure with intention.' UNION ALL
  SELECT 'Clarity and security.')
SELECT * FROM Words WHERE value LIKE ANY ('Intend%', '%intention%');

/*------------------------+
 | value                  |
 +------------------------+
 | Intend with clarity.   |
 | Secure with intention. |
 +------------------------*/

The following example checks to see if the %ity% pattern exists in a value and produces that value if the pattern is found.

Example with LIKE ALL:

WITH Words AS
 (SELECT 'Intend with clarity.' as value UNION ALL
  SELECT 'Secure with intention.' UNION ALL
  SELECT 'Clarity and security.')
SELECT * FROM Words WHERE value LIKE ALL ('%ity%');

/*-----------------------+
 | value                 |
 +-----------------------+
 | Intend with clarity.  |
 | Clarity and security. |
 +-----------------------*/

The following example checks to see if the %ity% pattern exists in a value produces that value if the pattern is not found:

WITH Words AS
 (SELECT 'Intend with clarity.' as value UNION ALL
  SELECT 'Secure with intention.' UNION ALL
  SELECT 'Clarity and security.')
SELECT * FROM Words WHERE value NOT LIKE ('%ity%');

/*------------------------+
 | value                  |
 +------------------------+
 | Secure with intention. |
 +------------------------*/

You can use a subquery as an expression in patterns. For example:

WITH Words AS
 (SELECT 'Intend with clarity.' as value UNION ALL
  SELECT 'Secure with intention.' UNION ALL
  SELECT 'Clarity and security.')
SELECT * FROM Words WHERE value LIKE ANY ((SELECT '%ion%'), '%and%');

/*------------------------+
 | value                  |
 +------------------------+
 | Secure with intention. |
 | Clarity and security.  |
 +------------------------*/

You can pass in a subquery for patterns. For example:

WITH Words AS
 (SELECT 'Intend with clarity.' as value UNION ALL
  SELECT 'Secure with intention.' UNION ALL
  SELECT 'Clarity and security.')
SELECT * FROM Words WHERE value LIKE ANY (SELECT '%with%');

/*------------------------+
 | value                  |
 +------------------------+
 | Intend with clarity.   |
 | Secure with intention. |
 +------------------------*/

You can pass in an array for patterns. For example:

WITH Words AS
 (SELECT 'Intend with clarity.' as value UNION ALL
  SELECT 'Secure with intention.' UNION ALL
  SELECT 'Clarity and security.')
SELECT * FROM Words WHERE value LIKE ANY UNNEST(['%ion%', '%and%']);

/*------------------------+
 | value                  |
 +------------------------+
 | Secure with intention. |
 | Clarity and security.  |
 +------------------------*/

You can pass in an array and subquery for patterns. For example:

WITH Words AS
 (SELECT 'Intend with clarity.' as value UNION ALL
  SELECT 'Secure with intention.' UNION ALL
  SELECT 'Clarity and security.')
SELECT *
FROM Words
WHERE
  value LIKE ANY UNNEST(ARRAY(SELECT e FROM UNNEST(['%ion%', '%and%']) AS e));

/*------------------------+
 | value                  |
 +------------------------+
 | Secure with intention. |
 | Clarity and security.  |
 +------------------------*/

The following queries illustrate some of the semantic rules for the quantified LIKE operator:

SELECT
  NULL LIKE ANY ('a', 'b'), -- NULL
  'a' LIKE ANY ('a', 'c'), -- TRUE
  'a' LIKE ANY ('b', 'c'), -- FALSE
  'a' LIKE ANY ('a', NULL), -- TRUE
  'a' LIKE ANY ('b', NULL), -- NULL
  NULL NOT LIKE ANY ('a', 'b'), -- NULL
  'a' NOT LIKE ANY ('a', 'b'), -- TRUE
  'a' NOT LIKE ANY ('a', '%a%'), -- FALSE
  'a' NOT LIKE ANY ('a', NULL), -- NULL
  'a' NOT LIKE ANY ('b', NULL); -- TRUE
SELECT
  NULL LIKE SOME ('a', 'b'), -- NULL
  'a' LIKE SOME ('a', 'c'), -- TRUE
  'a' LIKE SOME ('b', 'c'), -- FALSE
  'a' LIKE SOME ('a', NULL), -- TRUE
  'a' LIKE SOME ('b', NULL), -- NULL
  NULL NOT LIKE SOME ('a', 'b'), -- NULL
  'a' NOT LIKE SOME ('a', 'b'), -- TRUE
  'a' NOT LIKE SOME ('a', '%a%'), -- FALSE
  'a' NOT LIKE SOME ('a', NULL), -- NULL
  'a' NOT LIKE SOME ('b', NULL); -- TRUE
SELECT
  NULL LIKE ALL ('a', 'b'), -- NULL
  'a' LIKE ALL ('a', '%a%'), -- TRUE
  'a' LIKE ALL ('a', 'c'), -- FALSE
  'a' LIKE ALL ('a', NULL), -- NULL
  'a' LIKE ALL ('b', NULL), -- FALSE
  NULL NOT LIKE ALL ('a', 'b'), -- NULL
  'a' NOT LIKE ALL ('b', 'c'), -- TRUE
  'a' NOT LIKE ALL ('a', 'c'), -- FALSE
  'a' NOT LIKE ALL ('a', NULL), -- FALSE
  'a' NOT LIKE ALL ('b', NULL); -- NULL

The following queries illustrate some of the semantic rules for the quantified LIKE operator and collation:

SELECT
  COLLATE('a', 'und:ci') LIKE ALL ('a', 'A'), -- TRUE
  'a' LIKE ALL (COLLATE('a', 'und:ci'), 'A'), -- TRUE
  'a' LIKE ALL ('%A%', COLLATE('a', 'und:ci')); -- TRUE
-- ERROR: BYTES and STRING values can't be used together.
SELECT b'a' LIKE ALL (COLLATE('a', 'und:ci'), 'A');

NEW operator

The NEW operator only supports protocol buffers and uses the following syntax:

  • NEW protocol_buffer {...}: Creates a protocol buffer using a map constructor.
NEW protocol_buffer {
  field_name: literal_or_expression
  field_name { ... }
  repeated_field_name: [literal_or_expression, ... ]
}
  • NEW protocol_buffer (...): Creates a protocol buffer using a parenthesized list of arguments.

    NEW protocol_buffer(field [AS alias], ...field [AS alias])

Examples

The following example uses the NEW operator with a map constructor:

NEW Universe {
  name: "Sol"
  closest_planets: ["Mercury", "Venus", "Earth" ]
  star {
    radius_miles: 432,690
    age: 4,603,000,000
  }
  constellations: [{
    name: "Libra"
    index: 0
  }, {
    name: "Scorpio"
    index: 1
  }]
  all_planets: (SELECT planets FROM SolTable)
}

The following example uses the NEW operator with a parenthesized list of arguments:

SELECT
  key,
  name,
  NEW zetasql.examples.music.Chart(key AS rank, name AS chart_name)
FROM
  (SELECT 1 AS key, "2" AS name);

To learn more about protocol buffers in ZetaSQL, see Work with protocol buffers.

Concatenation operator

The concatenation operator combines multiple values into one.

Function Syntax Input Data Type Result Data Type
STRING || STRING [ || ... ] STRING STRING
BYTES || BYTES [ || ... ] BYTES BYTES
ARRAY<T> || ARRAY<T> [ || ... ] ARRAY<T> ARRAY<T>

Note: The concatenation operator is translated into a nested CONCAT function call. For example, 'A' || 'B' || 'C' becomes CONCAT('A', CONCAT('B', 'C')).

WITH expression

WITH(variable_assignment[, ...], result_expression)

variable_assignment:
  variable_name AS expression

Description

Create one or more variables. Each variable can be used in subsequent expressions within the WITH expression. Returns the value of result_expression.

  • variable_assignment: Introduces a variable. The variable name must be unique within a given WITH expression. Each expression can reference the variables that come before it. For example, if you create variable a, then follow it with variable b, you can reference a inside of b's expression.

    • variable_name: The name of the variable.

    • expression: The value to assign to the variable.

  • result_expression: An expression that is the WITH expression's result. result_expression can use all of the variables defined before it.

Return Type

  • The type of the result_expression.

Requirements and Caveats

  • A given variable may only be assigned once in a given WITH clause.
  • Variables created during WITH may not be used in analytic or aggregate function arguments. For example, WITH(a AS ..., SUM(a)) produces an error.
  • Volatile expressions (for example, RAND()) behave as if they are evaluated only once.

Examples

The following example first concatenates variable a with b, then variable b with c:

SELECT WITH(a AS '123',               -- a is '123'
            b AS CONCAT(a, '456'),    -- b is '123456'
            c AS '789',               -- c is '789'
            CONCAT(b, c)) AS result;  -- b + c is '123456789'

/*-------------*
 | result      |
 +-------------+
 | '123456789' |
 *-------------*/

In the following example, the volatile expression RAND() behaves as if it is evaluated only once. This means the value of the result expression will always be zero:

SELECT WITH(a AS RAND(), a - a);

/*---------*
 | result  |
 +---------+
 | 0.0     |
 *---------*/

Aggregate or analytic function results can be stored in variables. In this example, an average is computed:

SELECT WITH(s AS SUM(input), c AS COUNT(input), s/c)
FROM UNNEST([1.0, 2.0, 3.0]) AS input;

/*---------*
 | result  |
 +---------+
 | 2.0     |
 *---------*/

Variables cannot be used in aggregate or analytic function call arguments:

SELECT WITH(diff AS a - b, AVG(diff))
FROM UNNEST([
              STRUCT(1 AS a, 2 AS b),
              STRUCT(3 AS a, 4 AS b),
              STRUCT(5 AS a, 6 AS b),
            ]);

-- ERROR: WITH variables like 'diff' cannot be used in aggregate or analytic
-- function arguments.