メインコンテンツまでスキップ

バリアントデータのクエリー

備考

プレビュー

この機能は パブリック プレビュー段階です。

VARIANT 自己記述型のバイナリ形式でJSONのようなデータを格納する半構造化データ型です。VARIANTデータ型はDatabricks Runtime 15.3以降で使用できます。

Databricksでは、半構造化データにJSON文字列の代わりにVARIANTを使用することを推奨しています。現在JSON文字列を使用しており、移行を検討しているユーザーは、バリアントはJSON文字列とどう違うのですか?をご覧ください。

JSON 文字列として保存されている半構造化データをクエリするには、JSON 文字列のクエリを参照してください。

注記

VARIANT 列は、クラスタリングキー、パーティション、または Z-Order キーには使用できません。 VARIANT データ型は、比較、グループ化、順序付け、およびセット操作には使用できません。制限事項の完全なリストについては、 制限事項を参照してください。

バリアント列を持つテーブルを作成する

バリアント列を作成するには、 parse_json関数 ( SQLまたはPython ) を使用します。

以下を実行して、高度にネストされたデータがVARIANTとして保存されたテーブルを作成します。(このデータはこのページの他の例でも使用されます。)

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

バリアント列のクエリフィールド

バリアント列からフィールドを抽出するには、抽出パスで JSON フィールドの名前を指定してvariant_get関数 ( SQLまたはPython ) を使用します。フィールド名では常に大文字と小文字が区別されます。

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

variant_get の SQL 省略形

Databricks で JSON 文字列やその他の複雑なデータ型をクエリするための SQL 構文は、次のものを含め、 VARIANTデータに適用されます。

  • :を使用して、最上位のフィールドを選択します。
  • . または [<key>] を使用して、名前付きキーを持つネストされたフィールドを選択します。
  • [<index>] を使用して、配列から値を選択します。
SQL
SELECT raw:owner FROM store_data
+-------+
| owner |
+-------+
| "amy" |
+-------+
SQL
-- Use backticks to escape special characters.
SELECT raw:`zip code`, raw:`fb:testid` FROM store_data
+----------+-----------+
| zip code | fb:testid |
+----------+-----------+
| "94025" | "1234" |
+----------+-----------+

フィールド名にピリオド (.) が含まれている場合は、角括弧 ([ ]) でエスケープする必要があります。 たとえば、次のクエリは zip.code という名前のフィールドを選択します。

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

抽出バリアントのネストされたフィールド

バリアント列からネストされたフィールドを抽出するには、ドット表記または括弧を使用して指定します。フィールド名では常に大文字と小文字が区別されます。

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

パスが見つからない場合、結果は VariantVal型のnullになります。

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

バリアント配列から値を抽出する

配列から要素を抽出するには、括弧でインデックスを付けます。インデックスは 0 から始まります。

Python
# Index elements
df_variant.select((variant_get(col("raw"), "$.store.fruit[0]", "string")),(variant_get(col("raw"), "$.store.fruit[1]", "string"))).display()
+-------------------+------------------+
| fruit | fruit |
+-------------------+------------------+
| { | { |
| "type":"apple", | "type":"pear", |
| "weight":8 | "weight":9 |
| } | } |
+-------------------+------------------+

パスが見つからない場合、または配列インデックスが範囲外の場合、結果は null になります。

Pythonでバリアントを扱う

Spark DataFrames からバリアントをVariantValとして Python に抽出し、 toPythonメソッドとtoJsonメソッドを使用して個別に操作することができます。

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

VariantValを JSON 文字列として出力します。

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

VariantValを Python オブジェクトに変換します。

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

VariantVal.parseJson関数を使用してVariantValを構築することもできます。

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

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

バリアントを JSON 文字列として出力します。

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

バリアントを Python オブジェクトに変換し、値を出力します。

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

バリアントのスキーマを返す

バリアントのスキーマを返すには、 schema_of_variant関数 ( SQLまたはPython ) を使用します。

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

グループ内のすべてのバリアントの結合されたスキーマを返すには、 schema_of_variant_agg関数 ( SQLまたはPython ) を使用します。

次の例では、サンプル データ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()
+-----------------------------------------------------------------+
| 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()
+----------------------------------------------------------------------------------------------------------------------------+
| schema_of_variant(v) |
+----------------------------------------------------------------------------------------------------------------------------+
| OBJECT<age: BIGINT, department: STRING, id: BIGINT, in_stock: BOOLEAN, name: STRING, price: DECIMAL(5,1), product: STRING> |
+----------------------------------------------------------------------------------------------------------------------------+

バリアントオブジェクトと配列のフラット化

variant_explodeテーブル値ジェネレーター関数 ( SQLまたはPython ) を使用して、バリアント配列とオブジェクトをフラット化できます。

テーブル値関数 (TVF) DataFrame API を使用して、バリアントを複数の行に拡張します。

Python
spark.tvf.variant_explode(parse_json(lit(store_data))).display()
Python
# 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()

バリアント型のキャストルール

配列とスカラーは、 VARIANT 型を使用して格納できます。 バリアント型を他の型にキャストしようとすると、通常のキャストルールが個々の値とフィールドに適用され、さらに次のルールが追加されます。

注記

variant_get try_variant_get型引数を受け取り、これらのキャスト規則に従う必要があります。

元の型

挙動

VOID

結果は、タイプ VARIANTNULLになります。

ARRAY<elementType>

elementTypeは、 VARIANTにキャストできる型である必要があります。

schema_of_variant または schema_of_variant_aggを使用して型を推論する場合、解決できない競合する型が存在する場合STRING関数は VARIANT 型ではなく型にフォールバックします。

try_variant_get関数 ( Python ) を使用してキャストします。

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

また、キャストの失敗を処理するには、 try_variant_get関数 ( SQLまたはPython ) を使用します。

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

バリアントの null ルール

バリアント値がバリアント null であるかどうかを確認するには、 is_variant_null関数 ( SQLまたはPython ) を使用します。

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

その他のリソース