実践を通じて学ぶSpark SQL

Spark SQLとは結局何か？ふと思い立ちました。

https://www.databricks.com/jp/glossary/what-is-spark-sql

> Spark SQL は、Spark での SQL のネイティブサポートを可能にし、RDD（Sparkの分散データセット）や外部ソースに保存されたデータのクエリ処理を効率化します。また、RDD をリレーショナルテーブルと同様に処理することが可能です。このような優れた抽象化の統合により、開発者は複雑な分析で外部データをクエリする SQL コマンドを、容易に、単一のアプリケーション内に混在させることができます。具体的には、Spark SQL で開発者は次のことができるようになります。
>
> - Parquet ファイルや Hive テーブルからのリレーショナルデータのインポート
> - インポートされたデータと既存の RDD に対する SQL クエリの実行
> - Hive テーブルや Parquet ファイルへの容易な RDD の書き出し

わかるようでわからない。

こちらの記事で触れているSparkコアAPIのSQLがSpark SQL。ということは、大雑把に言えばSparkのSQL APIということ。

https://qiita.com/taka_yayoi/items/31190da754106b2d284e

![Screenshot 2023-08-16 at 13.04.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/01c2bbad-d03d-d284-5cee-ba214117eb69.png)

こちらにも書かれているように、Sparkに対してSQLで問い合わせを可能にしているモジュールとのこと。

https://spark.apache.org/sql/

```py
results = spark.sql(
  "SELECT * FROM people")
```

でも、そうすると気になるのは、上のSELECT文のテーブル`people`がどこにあるのかをSparkは知っているのか？ということです。Sparkは通常ファイルの読み書きを行いますが、それはファイルパスを指定しているからです。テーブル名自体にはファイルパスという概念がないので、テーブルの実態がファイルだとしても、テーブルとファイルパスの関係性を管理しなくてはなりません。

それを解決しているのが**メタストア**。名前の通り、テーブルのメタ情報を格納してくれています。SparkのネイティブのメタストアはHiveメタストアなので、こちらを使ってテーブルを管理します。ちなみにDatabricksのメタストアは[Unity Catalog](https://www.databricks.com/jp/product/unity-catalog)で管理されています。

![](https://spark.apache.org/images/sql-hive-arch.png)

前振りが長くなりましたが、Spark SQLを実際に実行していきます。Databricksで実行していきます。Sparkをローカルで実行している場合、`SparkSession`の取得やメタストアの設定等が必要ですが、Databricksの場合はお手軽にスタートできます。

以下のドキュメントを参考にして、Databricksに合わせて実行していきます。

https://spark.apache.org/docs/latest/sql-data-sources-hive-tables.html

# テーブルの作成

PythonからSQLを実行する際には、[`SparkSession.sql`](https://spark.apache.org/docs/latest/api/python/reference/pyspark.sql/api/pyspark.sql.SparkSession.sql.html)を使います。なお、DatabricksではSparkSessionは変数`spark`に格納されているので`spark.sql`になります。

テーブルを作成する前にデフォルトのデータベースを選択します。Databricksではテーブルの[名前空間が3レベル](https://docs.databricks.com/ja/data-governance/unity-catalog/index.html#the-unity-catalog-object-model)になっており、テーブルを指定する際には`カタログ.データベース(スキーマ).テーブル`となります。データベースを選択する際には上位の2レベルを指定します。

```py:Python
spark.sql("USE takaakiyayoi_catalog.spark_sql")
```

テーブルを作成します。[`CREATE TABLE`](https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-ddl-create-table-using.html)を使います。

```py:Python
spark.sql("CREATE TABLE IF NOT EXISTS src (key INT, value STRING)")
```

[カタログエクスプローラ](https://docs.databricks.com/ja/catalog-explorer/index.html)でテーブルを確認します。
![Screenshot 2024-02-26 at 21.25.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/b4af6145-2421-4985-89d7-b794df135e56.png)

# テーブルへの挿入

[`INSERT`](https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-dml-insert-into.html)を使います。

```py:Python
spark.sql("INSERT INTO src(key, value) VALUES (238,'val_238'), (86,'val_86')")
```

データがインサートされました。
![Screenshot 2024-02-26 at 21.30.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/e14cfe6b-a8ba-ec25-398a-750cb8bfd0bf.png)

# テーブルの読み込み

[`SELECT`](https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-qry-select.html)を使います。Sparkデータフレームが返却されるので`show`で表示します。

```py:Python
spark.sql("SELECT * FROM src").show()
```
```
+---+-------+
|key|  value|
+---+-------+
|238|val_238|
| 86| val_86|
+---+-------+
```

# テーブルの削除

[`DROP TABLE`](https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-ddl-drop-table.html)です。

```py:Python
spark.sql("DROP TABLE src")
```

# ちょっと待ってください

なぜ、SQLなのにPySpark経由で呼び出しているのでしょうか？`SparkSession`というエントリーポイントがあって初めてクエリーを実行できるので、PythonやScalaでエントリーポイントを作成してからという話は致し方ないとも言えます(詳しい方ツッコミあればお願いします)。

でも、Databricksならノートブックやクエリーエディタで直接SQLを実行できます。上のクエリーを直接SQLで実行していきましょう。上のクエリーはPythonノートブックで実行していたので、[マジックコマンド`%sql`](https://docs.gcp.databricks.com/ja/notebooks/notebooks-code.html#mix-languages)を使用してデフォルト言語を切り替えます。

```sql:SQL
%sql
CREATE TABLE IF NOT EXISTS src (key INT, value STRING)
```
テーブルが作成されました。
![Screenshot 2024-02-26 at 21.38.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/4ad36c6f-aaf1-7f51-ade8-8de2b0df719a.png)

```sql:SQL
%sql
INSERT INTO src(key, value) VALUES (238,'val_238'), (86,'val_86')
```
![Screenshot 2024-02-26 at 21.40.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/a86e3e41-0f27-3c01-c9af-7c3d4671b98b.png)

```sql:SQL
%sql
SELECT * FROM src
```

なお、SELECT文を実行した際には、結果は自動で表示されます。
![Screenshot 2024-02-26 at 21.40.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/68ef9ecb-2f9c-1834-c510-a0e0c6102072.png)

```sql:SQL
%sql
DROP TABLE src
```

ノートブックのデフォルト言語をSQLにすれば、マジックコマンド`%sql`も不要です。

実際に動かしてみると、「DHWのようにSQLを駆使してテーブルを操作できる」ことがわかるかと思います。しかも、エンジンがSparkなので、大量データであっても[高速にデータを操作](https://www.databricks.com/jp/blog/2021/11/02/databricks-sets-official-data-warehousing-performance-record.html)することができます。Databricks上であれば、ノートブックやカタログエクスプローラーを活用して段階的な開発も容易となります。ぜひトライしてみてください！

![Screenshot 2024-02-27 at 7.47.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/062b0d3a-69c5-bde2-d07e-4189d9df6800.png)

### はじめてのDatabricks

[はじめてのDatabricks](https://qiita.com/taka_yayoi/items/8dc72d083edb879a5e5d)

### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)