Consultar dados de variantes

Importante

Esse recurso está em uma versão prévia.

VARIANT é um tipo de dados semiestruturado que armazena dados semelhantes a JSON em um formato binário autodescrevendo. O tipo de dados VARIANT está disponível em Databricks Runtime 15.3 e superior.

Azure Databricks recomenda o uso VARIANT em cadeias de caracteres JSON para dados semiestruturados. Para os usuários que estão atualmente utilizando cadeias de caracteres JSON e que desejam migrar, consulte Como o 'variant' é diferente das cadeias de caracteres JSON?.

Para consultar dados semi-estruturados armazenados como cadeias de caracteres JSON, consulte cadeias de caracteres JSON de consulta.

Observação

VARIANT colunas não podem ser usadas para chaves de clustering, partições ou chaves de ordem Z. O tipo de dados VARIANT não pode ser usado para comparações, agrupamento, ordenação e operações de conjunto. Para obter uma lista completa de limitações, consulte Limitações.

Criar uma tabela com uma coluna variável

Para criar uma coluna variante, use a parse_json função (SQL ou Python).

Execute o seguinte para criar uma tabela com dados altamente aninhados armazenados como VARIANT. (Esses dados são usados em outros exemplos nesta página.)

Python

# Create a table with a variant column
store_data='''
{
  "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"
}
'''

# Create a DataFrame
df = spark.createDataFrame([(store_data,)], ["json"])

# Convert to a variant
df_variant = df.select(parse_json(col("json")).alias("raw"))

# Alternatively, create the DataFrame directly
# df_variant = spark.range(1).select(parse_json(lit(store_data)))

df_variant.display()

# Write out as a table
df_variant.write.saveAsTable("store_data")

SQL

-- Create a table with a variant column
CREATE TABLE store_data AS
SELECT parse_json(
  '{
    "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

SELECT * FROM store_data

Consultar campos em uma coluna variante

Para extrair campos de uma coluna variante, use a variant_get função (SQL ou Python) especificando o nome do campo JSON no caminho de extração. Os nomes de campo são sempre sensíveis a maiúsculas e minúsculas.

Python

# Extract a top-level field
df_variant.select(variant_get(col("raw"), "$.owner", "string")).display()

SQL

-- Extract a top-level field
SELECT variant_get(store_data.raw, '$.owner') AS owner FROM store_data

Você também pode usar a sintaxe SQL para consultar campos em uma coluna variante. Consulte a abreviação do SQL para variant_get.

Abreviação do SQL para variant_get

A sintaxe sql para consultar cadeias de caracteres JSON e outros tipos de dados complexos no Azure Databricks se aplica aos VARIANT dados, incluindo o seguinte:

  • Use : para selecionar campos de nível superior.
  • Use . ou [<key>] para selecionar campos aninhados com chaves nomeadas.
  • Use [<index>] para selecionar valores de matrizes.
SELECT raw:owner FROM store_data
+-------+
| owner |
+-------+
| "amy" |
+-------+
-- Use backticks to escape special characters.
SELECT raw:`zip code`, raw:`fb:testid` FROM store_data
+----------+-----------+
| zip code | fb:testid |
+----------+-----------+
| "94025"  | "1234"    |
+----------+-----------+

Se um nome de campo contiver um ponto (.), você deve evitá-lo com colchetes ([ ]). Por exemplo, a consulta apresentada a seguir seleciona um campo denominado zip.code:

SELECT raw:['zip.code'] FROM store_data

Extrair campos variantes aninhados

Para extrair campos aninhados de uma coluna variante, especifique-os usando notação de ponto ou colchetes. Os nomes de campo são sempre sensíveis a maiúsculas e minúsculas.

Python

# Use dot notation
df_variant.select(variant_get(col("raw"), "$.store.bicycle", "string")).display()
# Use brackets
df_variant.select(variant_get(col("raw"), "$.store['bicycle']", "string")).display()

Se um caminho não puder ser encontrado, o resultado será null do tipo VariantVal.

SQL

-- Use dot notation
SELECT raw:store.bicycle FROM store_data
-- Use brackets
SELECT raw:store['bicycle'] FROM store_data

Se um caminho não puder ser encontrado, o resultado será NULL do tipo VARIANT.

+-----------------+
| bicycle         |
+-----------------+
| {               |
| "color":"red",  |
| "price":19.95   |
| }               |
+-----------------+

Extrair valores de matrizes variantes

Para extrair elementos de matrizes, indexe com colchetes. Os índices são baseados em 0.

Python

# Index elements
df_variant.select((variant_get(col("raw"), "$.store.fruit[0]", "string")),(variant_get(col("raw"), "$.store.fruit[1]", "string"))).display()

SQL

-- Index elements
SELECT raw:store.fruit[0], raw:store.fruit[1] FROM store_data
+-------------------+------------------+
| fruit             | fruit            |
+-------------------+------------------+
| {                 | {                |
|   "type":"apple", |   "type":"pear", |
|   "weight":8      |   "weight":9     |
| }                 | }                |
+-------------------+------------------+

Se o caminho não puder ser encontrado ou se o índice de matriz estiver fora dos limites, o resultado será nulo.

Trabalhando com variantes no Python

Você pode extrair variantes dos Spark DataFrames no Python como VariantVal e trabalhar com elas individualmente usando os métodos toPython e toJson.

# toPython
data = [
    ('{"name": "Alice", "age": 25}',),
    ('["person", "electronic"]',),
    ('1',)
]

df_person = spark.createDataFrame(data, ["json"])

# Collect variants into a VariantVal
variants = df_person.select(parse_json(col("json")).alias("v")).collect()

Gere a saída de VariantVal como uma string JSON.

print(variants[0].v.toJson())
{"age":25,"name":"Alice"}

Converter um VariantVal em um objeto Python:

# First element is a dictionary
print(variants[0].v.toPython()["age"])
25
# Second element is a List
print(variants[1].v.toPython()[1])
electronic
# Third element is an Integer
print(variants[2].v.toPython())
1

Você também pode construir VariantVal usando a VariantVal.parseJson função.

# parseJson to construct VariantVal's in Python
from pyspark.sql.types import VariantVal

variant = VariantVal.parseJson('{"a": 1}')

Imprima a variante como uma cadeia de caracteres JSON:

print(variant.toJson())
{"a":1}

Converta a variante em um objeto Python e imprima um valor:

print(variant.toPython()["a"])
1

Retornar o esquema de uma variante

Para retornar o esquema de uma variante, use a schema_of_variant função (SQL ou Python).

Python

# Return the schema of the variant
df_variant.select(schema_of_variant(col("raw"))).display()

SQL

-- Return the schema of the variant
SELECT schema_of_variant(raw) FROM store_data;

Para retornar os esquemas combinados de todas as variantes em um grupo, use a schema_of_variant_agg função (SQL ou Python).

Os exemplos a seguir retornam o esquema e, em seguida, o esquema combinado para os dados json_datade exemplo.

Python


json_data = [
    ('{"name": "Alice", "age": 25}',),
    ('{"id": 101, "department": "HR"}',),
    ('{"product": "Laptop", "price": 1200.50, "in_stock": true}',)
]

df_item = spark.createDataFrame(json_data, ["json"])

# Return the schema
df_item.select(parse_json(col("json")).alias("v")).select(schema_of_variant(col("v"))).display()

SQL

CREATE OR REPLACE TEMP VIEW json_data AS
SELECT '{"name": "Alice", "age": 25}' AS json UNION ALL
SELECT '{"id": 101, "department": "HR"}' UNION ALL
SELECT '{"product": "Laptop", "price": 1200.50, "in_stock": true}';

-- Return the schema
SELECT schema_of_variant(parse_json(json)) FROM json_data;
+-----------------------------------------------------------------+
| schema_of_variant(v)                                            |
+-----------------------------------------------------------------+
| OBJECT<age: BIGINT, name: STRING>                               |
| OBJECT<department: STRING, id: BIGINT>                          |
| OBJECT<in_stock: BOOLEAN, price: DECIMAL(5,1), product: STRING> |
+-----------------------------------------------------------------+

Python

# Return the combined schema
df.select(parse_json(col("json")).alias("v")).select(schema_of_variant_agg(col("v"))).display()

SQL

-- Return the combined schema
SELECT schema_of_variant_agg(parse_json(json)) FROM json_data;
+----------------------------------------------------------------------------------------------------------------------------+
| schema_of_variant(v)                                                                                                       |
+----------------------------------------------------------------------------------------------------------------------------+
| OBJECT<age: BIGINT, department: STRING, id: BIGINT, in_stock: BOOLEAN, name: STRING, price: DECIMAL(5,1), product: STRING> |
+----------------------------------------------------------------------------------------------------------------------------+

Nivelar objetos e vetores variantes

A variant_explode função gerador com valor de tabela (SQL ou Python) pode ser usada para nivelar matrizes e objetos variantes.

Python

Use a API dataFrame da função com valor de tabela (TVF) para expandir uma variante em várias linhas:

spark.tvf.variant_explode(parse_json(lit(store_data))).display()
# To explode a nested field, first create a DataFrame with just the field
df_store_col = df_variant.select(variant_get(col("raw"), "$.store", "variant").alias("store"))

# Perform the explode with a lateral join and the outer function to return the new exploded DataFrame
df_store_exploded_lj = df_store_col.lateralJoin(spark.tvf.variant_explode(col("store").outer()))
df_store_exploded = df_store_exploded_lj.drop("store")
df_store_exploded.display()

SQL

Como variant_explode é uma função geradora, você a usa como parte da cláusula FROM em vez de na lista SELECT, como nos seguintes exemplos:

SELECT key, value
  FROM store_data,
  LATERAL variant_explode(store_data.raw);
SELECT pos, value
  FROM store_data,
  LATERAL variant_explode(store_data.raw:store.basket[0]);

Regras de conversão de tipo variante

Você pode armazenar matrizes e escalares usando o tipo VARIANT. Ao tentar converter tipos variantes para outros tipos, as regras de conversão normais se aplicam a valores e campos individuais, com as regras adicionais a seguir.

Observação

variant_get e try_variant_get usam argumentos de tipo e seguem as seguintes regras de conversão.

Tipo de origem Comportamento
VOID O resultado é um NULL de tipo VARIANT.
ARRAY<elementType> O elementType deve ser um tipo que pode ser convertido em VARIANT.

Ao inferir o tipo com schema_of_variant ou schema_of_variant_agg, as funções retornam ao tipo VARIANT em vez de STRING quando existem tipos conflitantes que não podem ser resolvidos.

Python

Use a try_variant_get função (Python) para converter:

# price is returned as a double, not a string
df_variant.select(try_variant_get(col("raw"), "$.store.bicycle.price", "double").alias("price"))
+------------------+
| price            |
+------------------+
| 19.95            |
+------------------+

SQL

Use a try_variant_get função (SQL) para converter:

-- price is returned as a double, not a string
SELECT try_variant_get(raw, '$.store.bicycle.price', 'double') as price FROM store_data
+------------------+
| price            |
+------------------+
| 19.95            |
+------------------+

Você também pode usar :: ou cast converter valores em tipos de dados com suporte:

-- cast into more complex types
SELECT cast(raw:store.bicycle AS STRUCT<price DOUBLE, color STRING>) bicycle FROM store_data;
-- `::` also supported
SELECT raw:store.bicycle::STRUCT<price DOUBLE, color STRING> bicycle FROM store_data;
+------------------+
| bicycle          |
+------------------+
| {                |
|   "price":19.95, |
|   "color":"red"  |
| }                |
+------------------+

Use também a try_variant_get função (SQL ou Python) para lidar com falhas de conversão:

Python

spark.range(1).select(parse_json(lit('{"a" : "c", "b" : 2}')).alias("v")).select(try_variant_get(col('v'), '$.a', 'boolean')).display()

SQL

SELECT try_variant_get(
  parse_json('{"a" : "c", "b" : 2}'),
  '$.a',
  'boolean'
)

Regras nulas de variantes

Use a is_variant_null função (SQL ou Python) para determinar se o valor da variante é uma variante nula.

Python

data = [
    ('null',),
    (None,),
    ('{"field_a" : 1, "field_b" : 2}',)
]

df = spark.createDataFrame(data, ["null_data"])
df.select(parse_json(col("null_data")).alias("v")).select(is_variant_null(col("v"))).display()
+------------------+
|is_variant_null(v)|
+------------------+
|              true|
+------------------+
|             false|
+------------------+
|             false|
+------------------+

SQL

As variantes podem conter dois tipos de nulos:

  • SQL NULL: SQLs NULL indicam que o valor está ausente. Esses são os mesmos NULLs que os usados ao lidar com dados estruturados.
  • Variante NULL: Variantes NULL indicam que a variante contém explicitamente um valor NULL. Eles não são iguais ao SQL NULLs, pois o valor NULL é armazenado nos dados.
SELECT
  is_variant_null(parse_json(NULL)) AS sql_null,
  is_variant_null(parse_json('null')) AS variant_null,
  is_variant_null(parse_json('{ "field_a": null }'):field_a) AS variant_null_value,
  is_variant_null(parse_json('{ "field_a": null }'):missing) AS missing_sql_value_null
+--------+------------+------------------+----------------------+
|sql_null|variant_null|variant_null_value|missing_sql_value_null|
+--------+------------+------------------+----------------------+
|   false|        true|              true|                 false|
+--------+------------+------------------+----------------------+

Recursos adicionais