web-dev-qa-db-ja.com

SparkSQL:同じクエリで2つの異なる変数を分解できますか?

次の分解クエリがありますが、正常に機能します。

data1 = sqlContext.sql("select explode(names) as name from data")

別のフィールド「色」を分解したいので、最終的な出力は名前と色のデカルト積になります。だから私はしました:

data1 = sqlContext.sql("select explode(names) as name, explode(colors) as color from data")

しかし、私はエラーを受け取りました:

 Only one generator allowed per select but Generate and and Explode found.;

誰かが何か考えを持っていますか?


2つのステップを実行することで、実際に機能させることができます。

   data1 = sqlContext.sql("select explode(names) as name from data")
   data1.registerTempTable('data1')
   data1 = sqlContext.sql("select explode(colors) as color from data1")

しかし、私はそれを1つのステップで行うことが可能かどうか疑問に思っていますか?どうもありがとう!

11
Edamame

正しい構文は

_select name, color 
from data 
lateral view explode(names) exploded_names as name 
lateral view explode(colors) exploded_colors as color
_

Rashidの回答が機能しなかった理由は、_LATERAL VIEW_によって生成されたテーブルに「名前」を付けなかったためです。

説明

このように考える:_LATERAL VIEW_は暗黙のJOINのように機能し、コレクション内のstructsからのすべての行に対して作成された一時テーブルが "表示"されます。したがって、構文を解析する方法は次のとおりです。

_LATERAL VIEW table_generation_function(collection_column) table_name AS col1, ...
_

複数の出力列

posexplode()などのテーブル生成関数を使用する場合、出力テーブルは1つですが、複数の出力列があります。

_LATERAL VIEW posexplode(orders) exploded_orders AS order_number, order
_

ネスティング

ネストされたコレクションを繰り返し展開することで_LATERAL VIEW_を「ネスト」することもできます。たとえば、

_LATERAL VIEW posexplode(orders) exploded_orders AS order_number, order
LATERAL VIEW posexplode(order.items) exploded_items AS item_number, item
_

パフォーマンスに関する考慮事項

_LATERAL VIEW_のトピックについては、SparkSQL経由で使用する方がDataFrame DSL経由で使用するよりも効率的であることに注意してください(例:myDF.explode())。その理由は、DSL APIが言語タイプとデータフレーム行の間のタイプ変換を実行する必要がある一方で、SQLがスキーマについて正確に推論できるためです。 DSL APIがパフォーマンスの面で失うものは何ですか。ただし、explodeからサポートされている型を返すことができるため、柔軟性が向上します。つまり、より複雑な変換を1つのステップで実行できます。

更新

Sparkの最近のバージョンでは、df.explode()による行レベルの分解が廃止され、df.select(..., explode(...).as(...))による列レベルの分解が優先されます。 explode_outer()もあり、展開される入力がnullであっても出力行を生成します。 Sparkは内部行データ表現を使用して完全に変換を実行できるため、列レベルの爆発は、上記の行レベルの爆発のパフォーマンスの問題の影響を受けません。

26
Sim

代わりに側面図を分解してみてください。

select name, color from data lateral view explode(names) as name lateral view explode(colors) as color;
2
Rashid Ali

spark sqlでは混乱を招くため、複数の分解は許可されていません。これは、爆発している2つのものの暗黙のデカルト積を取得するためです。複数の操作を実行する場合爆発すると、複数の選択を使用する必要があります。Hiveには、必要なものを達成できる側面ビューがあります(ここでのRashid ALiの回答で説明されています)。ここで、「データ」がデータフレームであると仮定します。

val data1 = data.select($"id",$"names",$explode($"colors").alias("colors"))
           //select required columns from colors 
            .select($"id",$"colors.field1",explode($"names").alias("names"))
            //now select required cols from names
            .select($"id",$"field1",$"names.col1",$"names.col2")

複数のデータフレームまたは上記のような単一のデータフレームで上記の選択を行うことができますが、パフォーマンスに影響はありません。

1
dheee

df.withColumnを使用して複数の列を分解する簡単な方法があります。

scala> val data = spark.sparkContext.parallelize(Seq((Array("Alice", "Bob"), Array("Red", "Green", "Blue"))))
  .toDF("names", "colors")
data: org.Apache.spark.sql.DataFrame = [names: array<string>, colors: array<string>]

scala> data.show
+------------+------------------+                                               
|       names|            colors|
+------------+------------------+
|[Alice, Bob]|[Red, Green, Blue]|
+------------+------------------+

scala> data.withColumn("name", explode('names))
  .withColumn("color", explode('colors))
  .show

+------------+------------------+-----+-----+
|       names|            colors| name|color|
+------------+------------------+-----+-----+
|[Alice, Bob]|[Red, Green, Blue]|Alice|  Red|
|[Alice, Bob]|[Red, Green, Blue]|Alice|Green|
|[Alice, Bob]|[Red, Green, Blue]|Alice| Blue|
|[Alice, Bob]|[Red, Green, Blue]|  Bob|  Red|
|[Alice, Bob]|[Red, Green, Blue]|  Bob|Green|
|[Alice, Bob]|[Red, Green, Blue]|  Bob| Blue|
+------------+------------------+-----+-----+
0
Todd Leo