JSON Path Expressions

This article describes the different elements that Exasol supports from the SQL/JSON path language, along with a short description and an example.

All parts of the SQL/JSON path language that are not mentioned in this section are not supported by Exasol.

Category: Prefix

The SQL/JSON path language supports the two prefixes 'strict' and 'lax' (also called JSON path modes). Exasol only supports the prefix 'lax'.

Lax

It is possible to add 'lax' as a prefix to any path. As Exasol only supports 'lax' path expressions, the prefix has no effect on the query result.

Examples

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, '{"first name" : "Claire", "surname" : "Doe"}');
INSERT INTO json_input VALUES(2, '{"first name" : "John", "age" : 20}');

SELECT id, JSON_VALUE(json, '$.surname') AS RESULT_VALUE FROM json_input;

Result

ID RESULT_VALUE
1 Doe
2 NULL
SELECT id, JSON_VALUE(json, 'lax $.surname') AS RESULT_VALUE FROM json_input;

Result

ID RESULT_VALUE
1 Doe
2 NULL

Category: Variables

The SQL/JSON path language supports several variables. As Exasol supports arbitrary expressions as json_path_expr, it does not need to support the PASSING clause and 'JSON path named variables'. As Exasol does not support filters, it does not support the '@' variable.

JSON Path Context Variable

The context variable '$' is the entry point for any JSON path.

Examples

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, 'true');
INSERT INTO json_input VALUES(2, 'false');
INSERT INTO json_input VALUES(3, 'null');
INSERT INTO json_input VALUES(4, '42');
INSERT INTO json_input VALUES(5, '1.0');
INSERT INTO json_input VALUES(6, '"Exasol"');

SELECT id, JSON_VALUE(json, '$') AS RESULT_VALUE FROM json_input;

Result

ID RESULT_VALUE
1 true
2 false
3 NULL
4 42
5 1.0
6 Exasol

Category: Accessors

With accessors it is possible to navigate through a JSON structure.

Member Accessor

A member accessor gives access to a JSON object member by its name. It is possible to concatenate an arbitrary number of names to access nested objects.

The member accessor is case-sensitive. If a name starts with the character '$' or the name contains special characters like white space, it is necessary to enclose the name with double-quotes. The member accessor adheres to the lexical rules of ECMAScript identifiers.

Examples

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, '{"customer" : { "first name" : "Ann" } }');
INSERT INTO json_input VALUES(2, '{"first name" : "Adam" }');
INSERT INTO json_input VALUES(4, '{"Forename" : "Luke" }');
INSERT INTO json_input VALUES(5, '{"$Forename" : "Sue" }');
INSERT INTO json_input VALUES(6, '{"Forename and Surname" : "John Doe" }');

SELECT id,
    JSON_VALUE(json, '$.customer."first name"') AS CUSTOMER_FORENAME,
    JSON_VALUE(json, '$."first name"') AS "first name",
    JSON_VALUE(json, '$.Forename') AS "Forename",
    JSON_VALUE(json, '$."$Forename"') AS "$Forename",
    JSON_VALUE(json, '$."Forename and Surname"') AS "Forename and Surname"
FROM json_input;

Result

ID CUSTOMER_FORENAME first name Forename $Forename Forename and Surname
1 Ann NULL NULL NULL NULL
2 NULL Adam NULL NULL NULL
3 NULL NULL Luke NULL NULL
4 NULL NULL NULL Sue NULL
5 NULL NULL NULL NULL John Doe

Wildcard Member Accessor

The wildcard member accessor '.*' gives access to all JSON object members independent from their name.

Examples

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, '{"customer" : { "first name" : "Ann" } }');
INSERT INTO json_input VALUES(2, '{"customer" : { "first name" : "John" } }');
INSERT INTO json_input VALUES(3, '{"customer" : { "first name" : "Ann", "surname" : "Doe" } }');

SELECT id,
       JSON_VALUE(json, '$.*.first name') AS FORENAME,
       JSON_VALUE(json, '$.*.*') AS DOUBLE_ASTERISK,
       JSON_VALUE(json, '$.*.surname') AS SURNAME
FROM json_input;

Result

ID FIRTS NAME DOUBLE_ASTERISK SURNAME
1 Ann Ann NULL
2 John John NULL
3 Ann NULL Doe

Element Accessor

With an element accessor, it is possible to select one or more indexes from a JSON array. Exasol supports access to single elements as well as ranges. It is also possible to combine single element accesses with range accesses. However, Exasol does not support overlapping or unordered indexes.

Examples

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, '["First", "Second", "Third"]');
INSERT INTO json_input VALUES(2, '[["First", "Second"], ["Third"]]');
INSERT INTO json_input VALUES(3, '[1, 2, 3, 4, {"name" : "Ann"}, 6]');

SELECT id,
    JSON_VALUE(json, '$[0]') AS FIRST_ELEMENT,
    JSON_VALUE(json, '$[2]') AS THIRD_ELEMENT,
    JSON_VALUE(json, '$[0][1]') AS NESTED_ELEMENT,
    JSON_VALUE(json, '$[4].name') AS NAME,
    JSON_VALUE(json, '$[1, 2 to 3, 4 to 5, 6].name') AS NAME_WITH_RANGE
FROM json_input;

Result

ID FIRST_ELEMENT THIRD_ELEMENT NESTED_ELEMENT NAME NAME_WITH_RANGE
1 First Third NULL NULL NULL
2 NULL NULL Second NULL NULL
3 1 3 NULL Ann Ann
SELECT id, JSON_VALUE(json, '$[0,0]') FROM json_input;
-- Returns error: data exception - invalid JSON path: array indices overlap

SELECT id, JSON_VALUE(json, '$[0 to 5, 1 to 7]') FROM json_input;
--returns error: data exception - invalid JSON path: array indices overlap

SELECT id, JSON_VALUE(json, '$[3, 1]') FROM json_input;
--returns error: data exception - invalid JSON path: array indices not in ascending order

Wildcard Element Accessor

The wildcard element accessor '[*]' gives access to all elements in an array. As Exasol always uses lax mode, it is not necessary to specify the wildcard element accessor for a single array.

It is only necessary to use the wildcard element accessor for nested arrays. If the wildcard element accessor is applied to a JSON value that is not an array, the lax mode wraps the value automatically into an array.

Examples

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, '{ "customers" : [ { "name" : "John" }, { "first name" : "Eve" } ] }');
INSERT INTO json_input VALUES(2, '{ "customers" : [ null, [ { "name" : "John" }, { "first name" : "Eve" } ] ] }');

SELECT id,
       JSON_VALUE(json, '$.customers[*].name') AS WILDCARD,
       JSON_VALUE(json, '$.customers.name') AS WITHOUT_WILDCARD,
       JSON_VALUE(json, '$.customers.name[*]') AS NON_ARRAY_WILDCARD,
       JSON_VALUE(json, '$.customers[*][*].name[*]') AS DOUBLE_WILDCARD
FROM json_input;

Result

ID WILDCARD WITHOUT_WILDCARD NON_ARRAY_WILDCARD DOUBLE_WILDCARD
1 John John John John
2 John NULL NULL John

Category: Item Functions

It is possible to call functions on an SQL/JSON value defined by a path. This section lists the predefined functions that Exasol supports.

Function type()

The function type() returns the type of the SQL/JSON value defined by a path. The return type is (VAR)CHAR.

Examples

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, NULL);
INSERT INTO json_input VALUES(2, '{"name" : "John"}');
INSERT INTO json_input VALUES(3, '[{"name" : "John"}]');
INSERT INTO json_input VALUES(4, 'true');
INSERT INTO json_input VALUES(5, 'false');
INSERT INTO json_input VALUES(6, 'null');
INSERT INTO json_input VALUES(7, '42');
INSERT INTO json_input VALUES(8, '1.0');
INSERT INTO json_input VALUES(9, '"Exasol"');

SELECT id, JSON_VALUE(json, '$.type()') AS JSON_RESULT
FROM json_input;

Result

ID JSON_RESULT
1 NULL
2 object
3 array
4 boolean
5 boolean
6 null
7 number
8 number
9 string

Function size()

The function size() returns the size of a JSON array. For all other JSON values it returns 1.

Examples

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, '{"first name" : "Ann", "surename" : "Smith"}');
INSERT INTO json_input VALUES(2, '["First", "Second", "Third"]');
INSERT INTO json_input VALUES(3, '[["First", "Second"], ["Third"]]');
INSERT INTO json_input VALUES(4, 'true');
INSERT INTO json_input VALUES(5, 'false');
INSERT INTO json_input VALUES(6, 'null');
INSERT INTO json_input VALUES(7, '42');
INSERT INTO json_input VALUES(8, '1.0');
INSERT INTO json_input VALUES(9, '"Exasol"');

SELECT id, json, JSON_VALUE(json, '$.size()') AS FUNCTION_SIZE
FROM json_input;

Result

ID JSON FUNCTION_SIZE
1 {"first name" : "Ann", "surename" : "Smith"} 1
2 ["First", "Second", "Third"] 3
3 ["First", "Second", "Third"] 2
4 true 1
5 false 1
6 null 1
7 42 1
8 1.0 1
9 "Exasol" 1

Category: Exasol Extensions

For manual exploration of JSON data and for debugging purposes, Exasol provides several extensions to the SQL/JSON path language. All of the extensions in this category are Exasol-specific and not part of the SQL standard.

Function json()

The function json() converts the JSON value (or values) specified by a path prefix into a JSON string. A possible application of json() is the extraction of complete JSON objects or JSON arrays as (VAR)CHAR. Note that json() removes insignificant white space between JSON elements from the original JSON input.

Examples

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, '{ "customers" : [ { "name" : "John Doe" }, { "first name" : "Eve" } ] }');
INSERT INTO json_input VALUES(2, '["First", "Second", "Third"]');
INSERT INTO json_input VALUES(3, '"Exasol"');

SELECT id,
       JSON_VALUE(json, '$.json()') AS COMPLETE_JSON,
       JSON_VALUE(json, '$.customers.json()') AS JSON_ARRAY,
       JSON_VALUE(json, '$.customers[0].json()') AS JSON_OBJECT
FROM json_input;

Result

ID COMPLETE_JSON JSON_ARRAY JSON_OBJECT
1 {"customers":[{"name":"John Doe"},{"first name":"Eve"}]} [{"name":"John Doe"},{"first name":"Eve"}] {"name":"John Doe"}
2 ["First","Second","Third"] NULL NULL
3 "Exasol" NULL NULL

Function path()

The function path() returns the actual path that Exasol uses as (VAR)CHAR. This can help to debug paths with wildcards.

Examples

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, '{ "customers" : [ { "name" : "John" } ] }');
INSERT INTO json_input VALUES(2, '{ "name list" : [ { "name" : "Ann" } ] }');
INSERT INTO json_input VALUES(3, '{ "customers" : [ null, null, { "name" : "Smith" } ] }');
INSERT INTO json_input VALUES(4, '{ "telephone list" : [ { "number" : "+49123456789" } ] }');

SELECT id,
       JSON_VALUE(json, '$.*.*') AS JSON_RESULT,
       JSON_VALUE(json, '$.*.*.path()') AS PATH_FUNCTION
FROM json_input;

Results

ID JSON_RESULT PATH_FUNCTION
1 John $.customers[0].name
2 Ann $."name list"[0].name
3 Smith $.customers[2].name
4 +49123456789 $."telephone list"[0].number

Function jsonpath()

The function jsonpath() determines the actual path that Exasol uses and splits it into an array of accessors. It returns a (VAR)CHAR result that contains a JSON array with the identified accessors of the path.

Examples

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, '{ "customers" : [ { "name" : "John" } ] }');
INSERT INTO json_input VALUES(2, '{ "name list" : [ { "name" : "Ann" } ] }');
INSERT INTO json_input VALUES(3, '{ "customers" : [ null, null,  { "name" : "Smith" } ] }');
INSERT INTO json_input VALUES(4, '{ "telephone list" : [ { "number" : "+49123456789" } ] }');

SELECT id,
    JSON_VALUE(json, '$.*.*') AS JSON_RESULT,
    JSON_VALUE(json, '$.*.*.path()') AS PATH_FUNCTION,
    JSON_VALUE(json, '$.*.*.jsonpath()') AS JSONPATH_FUNCTION
FROM json_input;

Results

ID JSON_RESULT PATH_FUNCTION JSONPATH_FUNCTION
1 John $.customers[0].name ["customers",0,"name"]
2 Ann $."name list"[0].name ["name list",0,"name"]
3 Smith $.customers[2].name ["customers",2,"name"]
4 +49123456789 $."telephone list"[0].number ["telephone list",0,"number"]

Function name()

The function name() returns the name of a name/value pair defined in a JSON object.

Examples

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, '{ "customers" : [ { "first name" : "John" } ] }');
INSERT INTO json_input VALUES(2, '{ "telephone list" : [ { "number" : "+49123456789" } ] }');

SELECT id,
       JSON_VALUE(json, '$.*.name()') AS JSON_NAME,
       JSON_VALUE(json, '$.*.*.name()') AS NESTED_JSON_NAME,
       JSON_VALUE(json, '$.*.*') AS NESTED_JSON_VALUE
FROM json_input;

Results

ID JSON_NAME NESTED_JSON_NAME NESTED_JSON_VALUE
1 customers first name John
2 telephone list number +49123456789

Function id()

The function id() prints a unique identifier for each JSON element. The identifier is only unique within one JSON input.

Examples

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, '[{"first name" : "Nick"}, {"first name" : "Eve"}]');
INSERT INTO json_input VALUES(2, '[{"first name" : "Ann"}, {"surename" : "Smith", "first name" : "John"}]');

SELECT id,
    JSON_VALUE(json, '$[0].id()') AS ELEMENT_ID_0,
    JSON_VALUE(json, '$[0]."first name".id()') AS NAME_ID_0,
    JSON_VALUE(json, '$[0]."first name"') AS VALUE_ID_0,
    JSON_VALUE(json, '$[1].id()') AS ELEMENT_ID_1,
    JSON_VALUE(json, '$[1]."first name".id()') AS NAME_ID_1,
    JSON_VALUE(json, '$[1]."first name"') AS VALUE_ID_1
FROM json_input;

Results

ID ELEMENT_ID_0 NAME_ID_0 VALUE_ID_0 ELEMENT_ID_1 NAME_ID_1 VALUE_ID_1
1 1 3 Nick 4 6 Eve
2 1 3 Ann 4 8 John

Function position()

The function position() returns the position of an element within an array. The index of the first element in an array is zero. Similarly, for elements that are not part of an array, position() returns 0. This can help in identifying specific data elements.

Examples

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, '[{"first name" : "Nick", "name" : "foo"}, {"first name" : "Eve"}]');
INSERT INTO json_input VALUES(2, '{"company" : "Exasol"}');
INSERT INTO json_input VALUES(3, '"Exasol"');

SELECT id,
       JSON_VALUE(json, '$[1].position()') AS POSITION_1,
       JSON_VALUE(json, '$[1]."first name".position()') AS FORENAME_POSITION,
       JSON_VALUE(json, '$.position()') AS CONTEXT_POSITION
FROM json_input;

Results

ID POSITION_1 FORENAME_POSITION CONTEXT_POSITION
1 1 0 0
2 NULL NULL 0
3 NULL NULL 0
CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, '{"customers" : [{"name" : "Nick"}, {"name" : "Eve"}]}');
INSERT INTO json_input VALUES(2, '{"customers" : [{"name" : "Ann"}, {"name" : "John"}]}');

SELECT id,
       JSON_EXTRACT(json, '$.customers#.position()', '$.customers#.name')
           EMITS (JSON_POSITION INT, NAME VARCHAR(100))
FROM json_input;

Results

ID JSON_POSITION NAME
1 0 Nick
1 1 Eve
2 0 Ann
2 1 John

Function toarray()

The function toarray() returns all SQL/JSON values identified by the path as an array. This is useful to identify paths that return more than one value.

Examples

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, '{"customers" : [{"name" : "Nick"}, {"name" : "Eve"}]}');
INSERT INTO json_input VALUES(2, '{"customers" : [{"name" : "Ann"}], "family" : [{"name" : "John"}]}');
INSERT INTO json_input VALUES(3, '{"contacts" : {"name" : "Chris", "name" : "Kate"}}');
INSERT INTO json_input VALUES(4, '"Exasol"');

SELECT id,
       JSON_VALUE(json, '$.customers.name.json().toarray()') as CUSTOMERS_AS_JSON,
       JSON_VALUE(json, '$.customers[*].json().toarray()') as CUSTOMER_ENTRIES,
       JSON_VALUE(json, '$.*.name.toarray()') as ALL_NAMES,
       JSON_VALUE(json, '$.*.name.json().toarray()') as ALL_NAMES_AS_JSON,
       JSON_VALUE(json, '$.toarray()') as CONTEXT_AS_ARRAY
FROM json_input;

Results

ID CUSTOMERS_AS_JSON CUSTOMER_ENTRIES ALL_NAMES ALL_NAMES_AS_JSON CONTEXT_AS_ARRAY
1 ["Nick","Eve"] [{"name":"Nick"},{"name":"Eve"}] [Nick,Eve] ["Nick","Eve"] NULL
2 ["Ann"] [{"name":"Ann"}] [Ann,John] ["Ann","John"] NULL
3 [] [] [Chris,Kate] ["Chris","Kate"] NULL
4 [] [] [] [] [Exasol]

Multi-Level Wildcard Accessor

With this accessor it is possible to select all leaf values of a JSON input at once.

Examples

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, '{"customers" : [{"name" : "Nick"}, {"name" : "Eve"}]}');
INSERT INTO json_input VALUES(2, '{"customers" : [{"name" : "Ann"}], "family" : [{"name" : "John"}]}');
INSERT INTO json_input VALUES(3, '{"contacts" : {"name" : "Chris", "name" : "Kate"}, "telephone list" : [ { "number" : "+49123456789" } ] } ');
INSERT INTO json_input VALUES(4, '"Exasol"');
INSERT INTO json_input VALUES(5, '[1, 2, 3, 4, 5, {"measurement" : {"run" : "first run", "value" : 42.0}}]');

SELECT id, JSON_VALUE(json, '$.**.json().toarray()') as CUSTOMERS_AS_JSON
FROM json_input;

Results

ID CUSTOMERS_AS_JSON
1 ["Nick","Eve"]
2 ["Ann","John"]
3 ["Chris","Kate","+49123456789"]
4 ["Exasol
5 [1,2,3,4,5,"first run",42.0]

Category: JSON_EXTRACT Extensions

JSON_EXTRACT is an Exasol-specific function to convert JSON into a relational format during ETL jobs. For this purpose, there are several path extensions that are only available in JSON_EXTRACT.

Extract Multiple Rows From Nested Data

With JSON_EXTRACT it is possible to extract several rows from a single JSON input. If a path specifies more than one JSON value, it is possible to split the values into several rows with the '#' path extension. However, a single JSON_EXTRACT call can only split up paths inside the same hierarchy chain. It is not possible to split up two independent paths within the same JSON_EXTRACT call.

Examples

CREATE OR REPLACE TABLE json_input2(id INT, json VARCHAR(2000000));
INSERT INTO json_input2 VALUES(1, '{"data" : [{"country" : "United Kingdom", "customers" : [{"name" : "John Doe", "phone numbers" : [{"type" : "private", "number" : "+4412345678"}]}]}]}');
INSERT INTO json_input2 VALUES(2, '{"data" : [{"country" : "Germany", "customers" : [{"name" : "Hans Meier", "phone numbers" : [{"type" : "private", "number" : "+4912345678"}, {"type" : "mobile", "number" : "+49987654321"}]} ,{"name" : "Achim Schmidt", "phone numbers" : [{"type" : "private", "number" : "+49214365879"}, {"type" : "mobile", "number" : "+4989674523"}]}]}]}');

SELECT id, 
       JSON_EXTRACT(json,
            '$.data.country'
            '$.data.customers#.name'
            '$.data.customers#."phone numbers"#.type'
            '$.data.customers#."phone numbers"#.number'
        )
        EMITS(
              country VARCHAR(100), 
              name VARCHAR(100), 
              phone_number_type VARCHAR(100), 
              phone_number VARCHAR(100)
        )
FROM json_input2
ORDER BY id, name, phone_number;

Results

ID COUNTRY NAME PHONE_NUMBER_TYPE PHONE_NUMBER
1 United Kingdom John Doe private +4412345678
2 Germany Achim Schmidt private +49214365879
2 Germany Achim Schmidt mobile +4989674523
2 Germany Hans Meier private +4912345678
2 Germany Hans Meier mobile +49987654321
CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, '{"customers" : {"name" : "Ann"}, "family" : {"name" : "Michael"}}');
INSERT INTO json_input VALUES(2, '{"customers" : [{"name" : "Eve"}, {"name" : "Joe"}], "family": [{"name" : "Sven"}]}');

SELECT id,
       JSON_EXTRACT(json, '$.*.name#.id()'
                   '$.*.name#'
        )
        EMITS (
                GENERATED_ID INT
                NAME VARCHAR(1000)
        )
FROM json_input;

Results

ID GENERATED_ID NAME
1 4 Ann
1 8 Micheal
2 5 Eve
2 8 Joe
2 13 Sven

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, '{"customers" : [{"name" : "Nick"}, {"name" : "Eve"}], "articles" : [{"item number" : 42}, {"item number" : 234}}]');

SELECT id,
       JSON_EXTRACT(json, 
            '$.customers#.name'
            '$.articles#."item number"'
        )
        EMITS (
               CUSTOMERS VARCHAR(1000), 
               ITEM_NUMBERS VARCHAR(1000)
        )
FROM json_input;
--ERROR: data exception - invalid JSON path: All nested path prefixes ending with a '#' character must be a prefix of the longest nested path.

Function error()

The default behavior of JSON_EXTRACT is ERROR ON EMPTY and ERROR ON ERROR for all paths. It is possible to switch that behavior to LOG ON EMPTY and LOG ON ERROR by adding the special path '$.error()' as last element to the JSON_EXTRACT arguments. If LOG ON EMPTY or LOG ON ERROR is specified, JSON_EXTRACT writes all errors into an error column.

Examples

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));

INSERT INTO json_input VALUES(1, '{"customers" : [{"name" : "Nick"}, {"name" : "Eve"}]}');

SELECT JSON_EXTRACT(json,
                    '$.articles."item number"'
                    ) 
            EMITS (
                ITEM_NUMBERS VARCHAR(1000)
        )
FROM json_input;

--ERROR: data exception - no SQL/JSON item in column: ITEM_NUMBERS
SELECT ID, 
        JSON_EXTRACT(json, 
                 '$.customers.name'
         ) 
         EMITS (
                CUSTOMERS VARCHAR(1000)
         )
FROM json_input;

--Error: data exception - more than one SQL/JSON item in column: CUSTOMERS
SELECT ID,
       JSON_EXTRACT(json, 
              '$.articles."item number"'
              '$.customers.name',
              '$.error()'
         )
         EMITS (
                ITEM_NUMBERS VARCHAR(1000), 
                CUSTOMERS VARCHAR(1000), 
                ERROR_COLUMN VARCHAR(1000)
         )
FROM json_input;

Results

ID ITEM_NUMBER CUSTOMERS ERROR_COLUMN
1 NULL NULL [{ "column" : "ITEM_NUMBERS", "error" : "no SQL/JSON item" }, { "column" : "CUSTOMERS", "error" : "more than one SQL/JSON item" }]

Error Handling Suffixes

In some cases it is beneficial to specify the error handling for each path separately in JSON_EXTRACT. For that purpose, Exasol provides error handling suffixes. It is possible to append '[NULL|LOG|ERROR] ON EMPTY' and '[NULL|LOG|ERROR] ON ERROR' to any path (except '$.error()') in JSON_EXTRACT. If used on a single path, '[NULL|LOG|ERROR] ON EMPTY' has to be specified before '[NULL|LOG|ERROR] ON ERROR'.

The following table shows the error behavior if json_path_expr points to a non-existing value.

ON EMPTY Suffix $.error() column Example Result
NONE NOT PRESENT

SELECT JSON_EXTRACT('{"name" : "John"}', '$.surname') EMITS (JSON_RESULT VARCHAR(1000));

Exception: data exception - no SQL/JSON item

ERROR NOT PRESENT

SELECT JSON_EXTRACT('{"name" : "John"}', '$.surname ERROR ON EMPTY') EMITS (JSON_RESULT VARCHAR(1000));

Exception: data exception - no SQL/JSON item

NULL NOT PRESENT

SELECT JSON_EXTRACT('{"name" : "John"}', '$.surname NULL ON EMPTY') EMITS (JSON_RESULT VARCHAR(1000));

NULL
LOG NOT PRESENT

SELECT JSON_EXTRACT('{"name" : "John"}', '$.surname LOG ON EMPTY') EMITS (JSON_RESULT VARCHAR(1000));

Exception: data exception - invalid JSON path: LOG ON EMPTY used without $.error() column

NONE PRESENT

SELECT JSON_EXTRACT('{"name" : "John"}', '$.surname', '$.error()') EMITS (JSON_RESULT VARCHAR(1000), ERROR_COLUMN VARCHAR(1000));

NULL, ERROR_COLUMN: [{ "column" : "JSON_RESULT", "error" : "no SQL/JSON item" }]

ERROR PRESENT

SELECT JSON_EXTRACT('{"name" : "John"}', '$.surname ERROR ON EMPTY', '$.error()') EMITS (JSON_RESULT VARCHAR(1000), ERROR_COLUMN VARCHAR(1000));

Exception: data exception - no SQL/JSON item

NULL PRESENT

SELECT JSON_EXTRACT('{"name" : "John"}', '$.surname NULL ON EMPTY', '$.error()') EMITS (JSON_RESULT VARCHAR(1000), ERROR_COLUMN VARCHAR(1000));

NULL
LOG PRESENT

SELECT JSON_EXTRACT('{"name" : "John"}', '$.surname LOG ON EMPTY', '$.error()') EMITS (JSON_RESULT VARCHAR(1000), ERROR_COLUMN VARCHAR(1000));

NULL, ERROR_COLUMN: [{ "column" : "JSON_RESULT", "error" : "no SQL/JSON item" }]

The following table shows the error behavior if the json_path_expr is valid but an error occurs during the JSON processing.

ON ERROR Suffix $.error() column Example Result
NONE NOT PRESENT

SELECT JSON_EXTRACT('{"name" : "John"}', '$.name') EMITS (JSON_RESULT INT);

Exception: data exception - cast error in column

ERROR NOT PRESENT

SELECT JSON_EXTRACT('{"name" : "John"}', '$.name ERROR ON ERROR') EMITS (JSON_RESULT INT);

Exception: data exception - cast error in column

NULL NOT PRESENT

SELECT JSON_EXTRACT('{"name" : "John"}', '$.name NULL ON ERROR') EMITS (JSON_RESULT INT);

NULL
LOG NOT PRESENT

SELECT JSON_EXTRACT('{"name" : "John"}', '$.name LOG ON ERROR') EMITS (JSON_RESULT INT);

Exception: data exception - invalid JSON path: ERROR ON EMPTY used without $.error() column

NONE PRESENT

SELECT JSON_EXTRACT('{"name" : "John"}', '$.name', '$.error()') EMITS (JSON_RESULT INT, ERROR_COLUMN VARCHAR(1000));

NULL, ERROR_COLUMN: [{ "column" : "JSON_RESULT", "error" : "cast error" }]

ERROR PRESENT

SELECT JSON_EXTRACT('{"name" : "John"}', '$.name ERROR ON ERROR', '$.error()') EMITS (JSON_RESULT INT, ERROR_COLUMN VARCHAR(1000));

Exception: data exception - cast error in column

NULL PRESENT

SELECT JSON_EXTRACT('{"name" : "John"}', '$.name NULL ON ERROR', '$.error()') EMITS (JSON_RESULT INT, ERROR_COLUMN VARCHAR(1000));

NULL
LOG PRESENT

SELECT JSON_EXTRACT('{"name" : "John"}', '$.name LOG ON ERROR', '$.error()') EMITS (JSON_RESULT INT, ERROR_COLUMN VARCHAR(1000));

NULL, ERROR_COLUMN: [{ "column" : "JSON_RESULT", "error" : "cast error" }]

Examples

CREATE OR REPLACE TABLE json_input(id INT, json VARCHAR(2000000));
INSERT INTO json_input VALUES(1, '{"customers" : [{"name" : "Nick"}, {"name" : "Eve"}], "articles" : [{"item number" : 10}, {"item number" : 234}]}');
INSERT INTO json_input VALUES(2, '{"articles" : [{"item number" : 42}]}');

SELECT ID,
       JSON_EXTRACT(json, 
              '$.customers.name LOG ON EMPTY NULL ON ERROR'
              '$.articles."item number" NULL ON EMPTY LOG ON ERROR'
              '$.error()'
         )
         EMITS (
                CUSTOMERS VARCHAR(1000), 
                ITEM_NUMBER VARCHAR(1000), 
                ERROR_COLUMN VARCHAR(1000)
         )
FROM json_input;

Results

ID CUSTOMERS ITEM_NUMBERS ERROR_COLUMN
1 NULL NULL [{ "column" : "ITEM_NUMBER", "error" : "more than one SQL/JSON item" }]
2 NULL 42 [{ "column" : "CUSTOMERS", "error" : "no SQL/JSON item" }]