Eseguire query su tipi nidificati nei file Parquet e JSON con il pool SQL serverless in Azure Synapse Analytics

Questo articolo illustra come scrivere una query usando il pool SQL serverless in Azure Synapse Analytics. La query leggerà i tipi nidificati Parquet. I tipi nidificati sono strutture complesse che rappresentano oggetti o matrici. I tipi nidificati possono essere archiviati in:

  • Parquet, in cui è possibile avere più colonne complesse che contengono matrici e oggetti.
  • File JSON gerarchici, in cui è possibile leggere un documento JSON complesso come una singola colonna.
  • Raccolte di Azure Cosmos DB (attualmente in anteprima pubblica controllata), in cui ogni documento può contenere proprietà annidate complesse.

Il pool SQL serverless formatta tutti i tipi nidificati come oggetti e matrici JSON. È quindi possibile estrarre o modificare oggetti complessi con funzioni JSON o analizzare i dati JSON con la funzione OPENJSON.

Ecco un esempio di query che estrae i valori scalari e di oggetto dal file JSON del set di dati per la ricerca aperta sul COVID-19, che contiene oggetti annidati:

SELECT
    title = JSON_VALUE(doc, '$.metadata.title'),
    first_author = JSON_QUERY(doc, '$.metadata.authors[0]'),
    first_author_name = JSON_VALUE(doc, '$.metadata.authors[0].first'),
    complex_object = doc
FROM
    OPENROWSET(
        BULK 'https://azureopendatastorage.blob.core.windows.net/covid19temp/comm_use_subset/pdf_json/000b7d1517ceebb34e1e3e817695b6de03e2fa78.json',
        FORMAT='CSV', FIELDTERMINATOR ='0x0b', FIELDQUOTE = '0x0b', ROWTERMINATOR = '0x0b'
    )
    WITH ( doc varchar(MAX) ) AS docs;

La funzione JSON_VALUE restituisce un valore scalare dal campo in corrispondenza del percorso specificato. La funzione JSON_QUERY restituisce un oggetto in formato JSON dal campo nel percorso specificato.

Importante

In questo esempio si usa un file del set di dati per la ricerca aperta sul COVID-19. Vedere qui per la licenza e la struttura dei dati.

Prerequisiti

Il primo passaggio consiste nel creare un database in cui verrà creata l'origine dati. Si inizializzeranno quindi gli oggetti eseguendo uno script di configurazione nel database. Questo script di configurazione creerà le origini dati, le credenziali con ambito database e i formati di file esterni usati negli esempi.

Proiettare dati annidati o ripetuti

Un file Parquet può avere più colonne con tipi complessi. I valori di queste colonne vengono formattati come testo JSON e restituiti come colonne VARCHAR. La query seguente legge il file structExample.parquet e mostra come leggere i valori delle colonne annidate:

SELECT
    DateStruct, TimeStruct, TimestampStruct, DecimalStruct, FloatStruct
FROM
    OPENROWSET(
        BULK 'parquet/nested/structExample.parquet',
        DATA_SOURCE = 'SqlOnDemandDemo',
        FORMAT='PARQUET'
    )
    WITH (
        DateStruct VARCHAR(8000),
        TimeStruct VARCHAR(8000),
        TimestampStruct VARCHAR(8000),
        DecimalStruct VARCHAR(8000),
        FloatStruct VARCHAR(8000)
    ) AS [r];

Questa query restituisce il risultato seguente. Il contenuto di ogni oggetto annidato viene restituito come testo JSON.

DateStruct TimeStruct TimestampStruct DecimalStruct FloatStruct
{"Date":"2009-04-25"} {"Time":"20:51:54.3598000"} {"Timestamp":"5501-04-08 12:13:57.4821000"} {"Decimal":11143412.25350} {"Float":0.5}
{"Date":"1916-04-29"} {"Time":"00:16:04.6778000"} {"Timestamp":"1990-06-30 20:50:52.6828000"} {"Decimal":1963545.62800} {"Float":-2.125}

La query seguente legge il file justSimpleArray.parquet Proietta tutte le colonne del file parquet, inclusi i dati annidati e ripetuti.

SELECT
    SimpleArray
FROM
    OPENROWSET(
        BULK 'parquet/nested/justSimpleArray.parquet',
        DATA_SOURCE = 'SqlOnDemandDemo',
        FORMAT='PARQUET'
    ) AS [r];

Questa query restituirà il risultato seguente:

SimpleArray
[11,12,13]
[21,22,23]

Leggere le proprietà dalle colonne di oggetti annidate

La funzione JSON_VALUE consente di restituire valori da colonne formattate come testo JSON:

SELECT
    title = JSON_VALUE(complex_column, '$.metadata.title'),
    first_author_name = JSON_VALUE(complex_column, '$.metadata.authors[0].first'),
    body_text = JSON_VALUE(complex_column, '$.body_text.text'),
    complex_column
FROM
    OPENROWSET( BULK 'https://azureopendatastorage.blob.core.windows.net/covid19temp/comm_use_subset/pdf_json/000b7d1517ceebb34e1e3e817695b6de03e2fa78.json',
                FORMAT='CSV', FIELDTERMINATOR ='0x0b', FIELDQUOTE = '0x0b', ROWTERMINATOR = '0x0b' ) WITH ( complex_column varchar(MAX) ) AS docs;

Il risultato è illustrato nella tabella seguente:

title first_author_name body_text complex_column
Supplementary Information An eco-epidemiolo... Julien - Figure S1 : Phylogeny of... { "paper_id": "000b7d1517ceebb34e1e3e817695b6de03e2fa78", "metadata": { "title": "Supplementary Information An eco-epidemiological study of Morbilli-related paramyxovirus infection in Madagascar bats reveals host-switching as the dominant macro-evolutionary mechanism", "authors": [ { "first": "Julien"

A differenza dei file JSON, che nella maggior parte dei casi restituiscono una singola colonna che contiene un oggetto JSON complesso, i file Parquet possono avere più colonne complesse. È possibile leggere le proprietà delle colonne annidate usando la funzione JSON_VALUE su ogni colonna. OPENROWSET consente di specificare direttamente i percorsi delle proprietà annidate in una clausola WITH. È possibile impostare i percorsi come nome di una colonna oppure aggiungere un'espressione di percorso JSON dopo il tipo di colonna.

La query seguente legge il file structExample.parquet e mostra come visualizzare gli elementi di una colonna annidata. Esistono due modi per fare riferimento a un valore annidato:

  • Specificando l'espressione di percorso del valore annidato dopo la specifica del tipo.
  • Formattando il nome della colonna come percorso annidato e usando "." per fare riferimento ai campi.
SELECT
    *
FROM
    OPENROWSET(
        BULK 'parquet/nested/structExample.parquet',
        DATA_SOURCE = 'SqlOnDemandDemo',
        FORMAT='PARQUET'
    )
    WITH (
        [DateValue] DATE '$.DateStruct.Date',
        [TimeStruct.Time] TIME,
        [TimestampStruct.Timestamp] DATETIME2,
        DecimalValue DECIMAL(18, 5) '$.DecimalStruct.Decimal',
        [FloatStruct.Float] FLOAT
    ) AS [r];

Accesso agli elementi di colonne ripetute

La query seguente legge il file justSimpleArray.parquet e usa JSON_VALUE per recuperare un elemento scalare dall'interno di una colonna ripetuta, ad esempio una matrice o un mapping:

SELECT
    *,
    JSON_VALUE(SimpleArray, '$[0]') AS FirstElement,
    JSON_VALUE(SimpleArray, '$[1]') AS SecondElement,
    JSON_VALUE(SimpleArray, '$[2]') AS ThirdElement
FROM
    OPENROWSET(
        BULK 'parquet/nested/justSimpleArray.parquet',
        DATA_SOURCE = 'SqlOnDemandDemo',
        FORMAT='PARQUET'
    ) AS [r];

Il risultato è il seguente:

SimpleArray FirstElement SecondElement ThirdElement
[11,12,13] 11 12 13
[21,22,23] 21 22 23

Accedere a oggetti secondari da colonne complesse

La query seguente legge il file mapExample.parquet e usa JSON_QUERY per recuperare un elemento non scalare dall'interno di una colonna ripetuta, ad esempio una matrice o un mapping:

SELECT
    MapOfPersons,
    JSON_QUERY(MapOfPersons, '$."John Doe"') AS [John]
FROM
    OPENROWSET(
        BULK 'parquet/nested/mapExample.parquet',
        DATA_SOURCE = 'SqlOnDemandDemo',
        FORMAT='PARQUET'
    ) AS [r];

È anche possibile fare riferimento in modo esplicito alle colonne da restituire in una clausola WITH:

SELECT DocId,
    MapOfPersons,
    JSON_QUERY(MapOfPersons, '$."John Doe"') AS [John]
FROM
    OPENROWSET(
        BULK 'parquet/nested/mapExample.parquet',
        DATA_SOURCE = 'SqlOnDemandDemo',
        FORMAT='PARQUET'
    ) 
    WITH (DocId bigint, MapOfPersons VARCHAR(max)) AS [r];

La struttura MapOfPersons viene restituita come colonna VARCHAR e formattata come stringa JSON.

Proiettare i valori da colonne ripetute

Se è disponibile una matrice di valori scalari (ad esempio [1,2,3]) in alcune colonne, è possibile espanderli facilmente e unirli con la riga principale usando questo script:

SELECT
    SimpleArray, Element
FROM
    OPENROWSET(
        BULK 'parquet/nested/justSimpleArray.parquet',
        DATA_SOURCE = 'SqlOnDemandDemo',
        FORMAT='PARQUET'
    ) AS arrays
    CROSS APPLY OPENJSON (SimpleArray) WITH (Element int '$') as array_values

Passaggi successivi

Il prossimo articolo illustra come eseguire query su file JSON.