Dotazování na data variant

Důležité

Tato funkce je ve verzi Public Preview.

VARIANT je částečně strukturovaný datový typ, který ukládá data podobná formátu JSON v binárním formátu, který popisuje sama sebe. Datový VARIANT typ je k dispozici v Databricks Runtime 15.3 a vyšší.

Azure Databricks doporučuje pro polostrukturovaná data používat VARIANT namísto řetězců JSON. Pro uživatele, kteří aktuálně používají řetězce JSON a chtějí migrovat, viz Jak se varianta liší od řetězců JSON?.

Pokud chcete dotazovat částečně strukturovaná data uložená jako řetězce JSON, přečtěte si téma Dotazování řetězců JSON.

Poznámka:

VARIANT sloupce nelze použít pro clusteringové klíče, oddíly nebo Z-řádové klíče. Datový typ VARIANT nelze použít pro porovnání, seskupování, řazení a operace nastavení. Úplný seznam omezení najdete v tématu Omezení.

Vytvoření tabulky s variantovým sloupcem

Pokud chcete vytvořit sloupec varianty, použijte parse_json funkci (SQL nebo Python).

Spuštěním následujícího příkazu vytvořte tabulku s vysoce vnořenými daty uloženými jako VARIANT. (Tato data se používají v jiných příkladech na této stránce.)

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

Dotazování polí ve variantním sloupci

Pokud chcete extrahovat pole z variantního sloupce, použijte variant_get funkci (SQL nebo Python) a zadejte název pole JSON v cestě extrakce. V názvech polí se vždy rozlišují malá a velká písmena.

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

Syntaxi SQL můžete také použít k dotazování polí ve sloupci varianty. Viz zkratka SQL pro variant_get.

Zkratka SQL pro variant_get

Syntaxe SQL pro dotazování řetězců JSON a dalších složitých datových typů v Azure Databricks se vztahuje na VARIANT data, včetně následujících:

  • Slouží : k výběru polí nejvyšší úrovně.
  • Použijte . nebo [<key>] k výběru vnořených polí s pojmenovanými klíči.
  • Slouží [<index>] k výběru hodnot z polí.
SELECT raw:owner FROM store_data
+-------+
| owner |
+-------+
| "amy" |
+-------+
-- Enclose a field name that contains special characters in single quotes inside square brackets.
SELECT raw:['zip code'], raw:['fb:testid'] FROM store_data
+----------+-----------+
| zip code | fb:testid |
+----------+-----------+
| "94025"  | "1234"    |
+----------+-----------+

Syntaxe ['<field>'] umožňuje escapovat libovolný speciální znak v názvu pole, včetně mezer, teček (.), dvojteček (:) a hranatých závorek ([ ]). Azure Databricks doporučuje tuto syntaxi pro každý název pole, který obsahuje speciální znak. Slouží raw:['zip.code'] například k výběru pole s názvem zip.codenebo raw:['A[1]'] k výběru pole s názvem A[1].

Backticks také escapují názvy polí, které obsahují mezery nebo dvojtečky, ale neescapují tečky ani hranaté závorky. Název pole, který obsahuje tečku nebo hranaté závorky, vrací při použití zpětných apostrofů pro escapování hodnotu NULL, proto pro takové názvy použijte syntaxi ['<field>'].

Chcete-li extrahovat pole, která obsahují speciální znaky v PySpark, použijte stejnou syntaxi závorek v variant_get cestě extrakce:

# Escape special characters in the extraction path
df_variant.select(variant_get(col("raw"), "$['zip.code']", "string"))
df_variant.select(variant_get(col("raw"), "$['A[1]']", "string"))

Extrahování vnořených polí variantních

Pokud chcete extrahovat vnořená pole ze sloupce varianty, zadejte je pomocí zápisu tečky nebo závorek. V názvech polí se vždy rozlišují malá a velká písmena.

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()

Pokud nelze najít cestu, je null výsledek typu VariantVal.

SQL

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

Pokud nelze najít cestu, je NULL výsledek typu VARIANT.

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

Extrahování hodnot z variantových polí

Chcete-li extrahovat prvky z polí, indexujte s hranatými závorkami. Indexy jsou založené na 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     |
| }                 | }                |
+-------------------+------------------+

Pokud cestu nelze najít nebo pokud je index pole mimo hranice, je výsledek null.

Práce s variantami v Pythonu

Varianty z datových rámců Sparku můžete extrahovat do Pythonu jako VariantVal a pracovat s nimi jednotlivě pomocí metod toPython a 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()

Výstup VariantVal jako řetězec JSON:

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

Převeďte VariantVal na objekt v Pythonu:

# 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

Můžete také vytvořit VariantVal pomocí VariantVal.parseJson funkce.

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

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

Vytiskněte variantu jako řetězec JSON:

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

Převeďte variantu na objekt Pythonu a vytiskněte hodnotu:

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

Vrácení schématu varianty

Pokud chcete vrátit schéma varianty, použijte schema_of_variant funkci (SQL nebo 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;

Pokud chcete vrátit kombinovaná schémata všech variant ve skupině, použijte schema_of_variant_agg funkci (SQL nebo Python).

Následující příklady vrátí schéma a potom kombinované schéma pro ukázková data json_data.

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> |
+----------------------------------------------------------------------------------------------------------------------------+

Zploštěné objekty a pole variant

Funkci generátoru variant_explode s hodnotami tabulky (SQL nebo Python) lze použít k zploštění variantových polí a objektů.

Python

Pomocí rozhraní API datového rámce TVF (table-valued function) můžete rozšířit variantu na více řádků:

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

Protože variant_explode je funkce generátoru, použijete ji jako součást FROM klauzule, nikoli v SELECT seznamu, jako v následujících příkladech:

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]);

Pravidla přetypování variantního typu

Pole a skaláry můžete ukládat pomocí VARIANT typu. Při pokusu o přetypování variantních typů na jiné typy platí normální pravidla přetypování pro jednotlivé hodnoty a pole s následujícími dalšími pravidly.

Poznámka:

variant_get a try_variant_get přijímají typové argumenty a řídí se těmito pravidly přetypování.

Typ zdroje Chování
VOID Výsledek je NULL typu VARIANT.
ARRAY<elementType> elementType musí být typ, na který lze přetypovat VARIANT.

Pokud existují konfliktní typy, které nelze vyřešit, při odvození typu pomocí schema_of_variant nebo schema_of_variant_agg funkce spadne zpět na VARIANT typ, místo STRING typu.

Python

try_variant_get K přetypování použijte funkci (Python):

# 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

try_variant_get K přetypování použijte funkci (SQL):

-- 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            |
+------------------+

Můžete také použít :: nebo cast k přetypování hodnot na podporované datové typy:

-- 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"  |
| }                |
+------------------+

Také použijte funkci try_variant_get (SQL nebo Python) k zpracování selhání přetypování.

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'
)

Variantní pravidla null

is_variant_null Pomocí funkce (SQL nebo Python) určete, jestli je hodnota varianty null.

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

Varianty můžou obsahovat dva druhy hodnot null:

  • SQL: SQL NULLNULL indikátory naznačují, že hodnota chybí. Jsou to stejné NULL jako při práci se strukturovanými daty.
  • Varianta: Varianty NULLNULL indikují, že varianta explicitně obsahuje NULL hodnotu. Nejedná se o stejné hodnoty jako sql NULL, protože NULL hodnota je uložená v datech.
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|
+--------+------------+------------------+----------------------+

Dodatečné zdroje