Eseguire query su dati semistrutturati in Azure Databricks

Questo articolo descrive gli operatori SQL di Databricks che è possibile usare per eseguire query e trasformare dati semistrutturati archiviati come JSON.

Nota

Questa funzionalità consente di leggere dati semistrutturati senza appiattire i file. Tuttavia, per ottenere prestazioni ottimali delle query di lettura, Databricks consiglia di estrarre colonne annidate con i tipi di dati corretti.

Si estrae una colonna da campi contenenti stringhe JSON usando la sintassi <column-name>:<extraction-path>, dove <column-name> è il nome della colonna stringa e <extraction-path> è il percorso del campo da estrarre. I risultati restituiti sono stringhe.

Creare una tabella con dati altamente annidati

Eseguire la query seguente per creare una tabella con dati altamente annidati. Gli esempi in questo articolo fanno riferimento a questa tabella.

CREATE TABLE store_data AS SELECT
'{
   "store":{
      "fruit": [
        {"weight":8,"type":"apple"},
        {"weight":9,"type":"pear"}
      ],
      "basket":[
        [1,2,{"b":"y","a":"x"}],
        [3,4],
        [5,6]
      ],
      "book":[
        {
          "author":"Nigel Rees",
          "title":"Sayings of the Century",
          "category":"reference",
          "price":8.95
        },
        {
          "author":"Herman Melville",
          "title":"Moby Dick",
          "category":"fiction",
          "price":8.99,
          "isbn":"0-553-21311-3"
        },
        {
          "author":"J. R. R. Tolkien",
          "title":"The Lord of the Rings",
          "category":"fiction",
          "reader":[
            {"age":25,"name":"bob"},
            {"age":26,"name":"jack"}
          ],
          "price":22.99,
          "isbn":"0-395-19395-8"
        }
      ],
      "bicycle":{
        "price":19.95,
        "color":"red"
      }
    },
    "owner":"amy",
    "zip code":"94025",
    "fb:testid":"1234"
 }' as raw

Estrarre una colonna di primo livello

Per estrarre una colonna, specificare il nome del campo JSON nel percorso di estrazione.

È possibile specificare nomi di colonna tra parentesi quadre. Le colonne a cui si fa riferimento tra parentesi quadre vengono confrontate con distinzione tra maiuscole e minuscole. Al nome della colonna viene fatto riferimento senza distinzione tra maiuscole e minuscole.

SELECT raw:owner, RAW:owner FROM store_data
+-------+-------+
| owner | owner |
+-------+-------+
| amy   | amy   |
+-------+-------+
-- References are case sensitive when you use brackets
SELECT raw:OWNER case_insensitive, raw:['OWNER'] case_sensitive FROM store_data
+------------------+----------------+
| case_insensitive | case_sensitive |
+------------------+----------------+
| amy              | null           |
+------------------+----------------+

Usare i backtick per spazi di escape e caratteri speciali. I nomi dei campi vengono confrontati senza distinzione tra maiuscole e minuscole.

-- Use backticks to escape special characters. References are case insensitive when you use backticks.
-- Use brackets to make them case sensitive.
SELECT raw:`zip code`, raw:`Zip Code`, raw:['fb:testid'] FROM store_data
+----------+----------+-----------+
| zip code | Zip Code | fb:testid |
+----------+----------+-----------+
| 94025    | 94025    | 1234      |
+----------+----------+-----------+

Nota

Se un record JSON contiene più colonne che possono corrispondere al percorso di estrazione a causa di corrispondenza senza distinzione tra maiuscole e minuscole, verrà visualizzato un errore che chiede di usare le parentesi quadre. Se sono presenti corrispondenze di colonne tra righe, non verranno visualizzati errori. Di seguito verrà generato un errore: {"foo":"bar", "Foo":"bar"}e il codice seguente non genererà un errore:

{"foo":"bar"}
{"Foo":"bar"}

Estrarre campi annidati

È possibile specificare campi annidati tramite notazione punto o parentesi quadre. Quando si usano parentesi quadre, le colonne vengono confrontate con distinzione tra maiuscole e minuscole.

-- Use dot notation
SELECT raw:store.bicycle FROM store_data
-- the column returned is a string
+------------------+
| bicycle          |
+------------------+
| {                |
|   "price":19.95, |
|   "color":"red"  |
| }                |
+------------------+
-- Use brackets
SELECT raw:store['bicycle'], raw:store['BICYCLE'] FROM store_data
+------------------+---------+
| bicycle          | BICYCLE |
+------------------+---------+
| {                | null    |
|   "price":19.95, |         |
|   "color":"red"  |         |
| }                |         |
+------------------+---------+

Estrarre valori da matrici

Gli elementi vengono indicizzati in matrici con parentesi quadre. Gli indici sono basati su 0. È possibile usare un asterisco (*) seguito dalla notazione punto o parentesi quadre per estrarre campi secondari da tutti gli elementi di una matrice.

-- Index elements
SELECT raw:store.fruit[0], raw:store.fruit[1] FROM store_data
+------------------+-----------------+
| fruit            | fruit           |
+------------------+-----------------+
| {                | {               |
|   "weight":8,    |   "weight":9,   |
|   "type":"apple" |   "type":"pear" |
| }                | }               |
+------------------+-----------------+
-- Extract subfields from arrays
SELECT raw:store.book[*].isbn FROM store_data
+--------------------+
| isbn               |
+--------------------+
| [                  |
|   null,            |
|   "0-553-21311-3", |
|   "0-395-19395-8"  |
| ]                  |
+--------------------+
-- Access arrays within arrays or structs within arrays
SELECT
    raw:store.basket[*],
    raw:store.basket[*][0] first_of_baskets,
    raw:store.basket[0][*] first_basket,
    raw:store.basket[*][*] all_elements_flattened,
    raw:store.basket[0][2].b subfield
FROM store_data
+----------------------------+------------------+---------------------+---------------------------------+----------+
| basket                     | first_of_baskets | first_basket        | all_elements_flattened          | subfield |
+----------------------------+------------------+---------------------+---------------------------------+----------+
| [                          | [                | [                   | [1,2,{"b":"y","a":"x"},3,4,5,6] | y        |
|   [1,2,{"b":"y","a":"x"}], |   1,             |   1,                |                                 |          |
|   [3,4],                   |   3,             |   2,                |                                 |          |
|   [5,6]                    |   5              |   {"b":"y","a":"x"} |                                 |          |
| ]                          | ]                | ]                   |                                 |          |
+----------------------------+------------------+---------------------+---------------------------------+----------+

Eseguire il cast dei valori

È possibile usare :: per eseguire il cast dei valori ai tipi di dati di base. Usare il metodo from_json per eseguire il cast dei risultati annidati in tipi di dati più complessi, ad esempio matrici o struct.

-- price is returned as a double, not a string
SELECT raw:store.bicycle.price::double FROM store_data
+------------------+
| price            |
+------------------+
| 19.95            |
+------------------+
-- use from_json to cast into more complex types
SELECT from_json(raw:store.bicycle, 'price double, color string') bicycle FROM store_data
-- the column returned is a struct containing the columns price and color
+------------------+
| bicycle          |
+------------------+
| {                |
|   "price":19.95, |
|   "color":"red"  |
| }                |
+------------------+
SELECT from_json(raw:store.basket[*], 'array<array<string>>') baskets FROM store_data
-- the column returned is an array of string arrays
+------------------------------------------+
| basket                                   |
+------------------------------------------+
| [                                        |
|   ["1","2","{\"b\":\"y\",\"a\":\"x\"}]", |
|   ["3","4"],                             |
|   ["5","6"]                              |
| ]                                        |
+------------------------------------------+

Comportamento NULL

Quando esiste un campo JSON con un null valore, si riceverà un valore SQL null per tale colonna, non un valore di null testo.

select '{"key":null}':key is null sql_null, '{"key":null}':key == 'null' text_null
+-------------+-----------+
| sql_null    | text_null |
+-------------+-----------+
| true        | null      |
+-------------+-----------+

Trasformare i dati annidati usando gli operatori Spark SQL

Apache Spark include diverse funzioni predefinite per l'uso di dati complessi e annidati. Il notebook seguente contiene esempi.

Inoltre, le funzioni di ordine superiore offrono molte opzioni aggiuntive quando gli operatori Spark predefiniti non sono disponibili per la trasformazione dei dati nel modo desiderato.

Notebook contenente dati complessi e annidati

Ottenere il notebook