Databricksノートブックで混在する言語間の変数の共有

Databricksのノートブックは、一つのノートブックの中にPython、SQL、シェルなどを混在させることができます。大規模開発であれば[ジョブ化](https://docs.databricks.com/ja/jobs/index.html)したり、[言語ごとのノートブックを作成して連携](https://docs.databricks.com/ja/notebooks/notebook-workflows.html)させることも可能ですが、それほど規模が大きくなく、かつ、ある箇所はSQL、別の箇所ではPythonで処理をしたいといったケースでは便利な機能です。

https://docs.databricks.com/ja/notebooks/notebooks-code.html#mix-languages

そして、複数言語が混在しているノートブックがある際に必要になってくるのが**変数の共有**です。Pythonの処理結果をSQLやシェルから参照したい場合にどうしたらいいのでしょうか。過去に以下の記事を書いていますが、今回はPythonからSQLの変数へのアクセスやシェルも含めてウォークスルーしていきます。

https://qiita.com/taka_yayoi/items/e447ba254a19bcd8b648

なお、[こちらの記事](https://medium.com/@robin.loche/how-to-work-with-multiple-languages-on-databricks-e22dea9f8c7)ではRなど他の言語との変数の共有方法についてもカバーされています。

この記事で言いたいことを図にまとめると以下のようになります。

![Screenshot 2024-12-23 at 13.12.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/cb0c1fe3-c143-f866-81e2-94b51d82ad75.png)

# SQLからPython変数へのアクセス

## Sparkパラメーターによるアクセス

いくつかアプローチがありますが、推奨は`spark.conf.set`を用いたSparkパラメーターによるものとなります。

```py
# Pythonセル
import datetime

x = str(datetime.date.today())
x
```
```
'2024-12-23'
```
```py
spark.conf.set('v.max_date', x)
```

```sql
%sql
-- SQLセル
SELECT "${v.max_date}" AS max_date
```
![Screenshot 2024-12-23 at 11.47.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/3cd99d1a-7b1d-d242-56cb-bef46465b72b.png)

## SQL変数経由でのアクセス

[SQL変数](https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-ddl-declare-variable.html)経由でも引き渡し可能です。

```py
# Pythonセル
my_var = 5
spark.sql("DECLARE OR REPLACE my_var INT")
spark.sql(f"SET VAR my_var = {my_var}")
```

```sql
%sql
-- SQLセル
SELECT my_var
```
![Screenshot 2024-12-23 at 11.56.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/60d77ce6-366c-4443-3a6e-6f0e2a91c18a.png)

## ウィジェット経由でのアクセス

[ウィジェット経由](https://docs.databricks.com/ja/notebooks/widgets.html#use-widget-values-in-spark-sql-and-sql-warehouse)でもアクセスできますが、[こちらの制限事項](https://docs.databricks.com/ja/notebooks/notebook-limitations.html#databricks-widgets)にあるように、**RUN ALL**した際やジョブにした際、期待した通りに動作しないリスクがあります。インタラクティブに実行する際にはこちらでも問題ありません。

```py
dbutils.widgets.text("max_date", x)
```

```sql
%sql
-- SQLセル
select getArgument("max_date") AS max_date -- SQLのgetArgument関数は廃止され、DBR 17.0以降では削除されます。

select "${max_date}" AS max_date -- パラメーターマーカーへの移行が推奨されています

select :max_date AS max_date -- パラメーターマーカーによるウィジェット変数へのアクセス
```
![Screenshot 2024-12-23 at 11.50.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/a105767c-0b4d-176e-cc4e-0ccfff14ff53.png)

# SQLからPythonのSparkデータフレームへのアクセス

これは[一時ビュー](https://docs.databricks.com/ja/views/index.html#temporary-views)を使うのが簡単です。

```py
# Pythonセル
df = spark.createDataFrame([(1, 'Alice'), (2, 'Bob')], ['id', 'name'])
df.createOrReplaceTempView("people")
```
```sql
%sql
-- SQLセル
SELECT * FROM people
```
![Screenshot 2024-12-23 at 13.01.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/7a7c3f53-c4d3-814b-3c18-6a390f45da25.png)



# PythonからSQLへのアクセス

ノートブックでSQLを実行すると、その結果は[暗黙的に作成されるデータフレーム`_sqldf`に格納](https://docs.databricks.com/ja/notebooks/notebooks-code.html#explore-sql-cell-results)されますので、そちらにアクセスします。

```sql
%sql
SELECT
  count(*) as count
FROM
  users.takaaki_yayoi.covid_cases;
```
![Screenshot 2024-12-23 at 12.02.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/db241436-49ff-4770-a044-70f2e209e250.png)

```py
# Pythonセル
pdf = _sqldf.toPandas()
pdf["count"][0]
```
```
23171
```

# シェルからPython変数へのアクセス

Databricksノートブックでは、`%sh`マジックコマンドで、クラスターのドライバーノード上のシェルにアクセスすることができます。[こちら](https://community.databricks.com/t5/data-engineering/how-to-pass-a-python-variables-to-shell-script/td-p/28332)で説明されているように、環境変数経由でシェルからPythonの変数にアクセスできます。

```py
# Pythonセル
import os
l =['A','B','C','D']
os.environ['LIST']=' '.join(l)
print(os.getenv('LIST'))
```

```sh
%sh
# シェルのセル
for i in $LIST
do
  echo $i
done
```
```
A
B
C
D
```

# Pythonからシェル変数へのアクセス

逆方向での直接的な方法はサポートされていないので、[こちら](https://stackoverflow.com/questions/75885945/how-to-pass-shell-script-variable-to-python-in-databricks)にあるようにファイル経由となります。ドライバーノードのローカルストレージに変数を書き込んだファイルを出力します。

```sh
%sh
# シェルのセル
echo "my_value" >/tmp/foo
```

```py
# Pythonセル
from pathlib import Path
my_var = Path("/tmp/foo").read_text()
print(my_var)
```
```
my_value
```


### はじめてのDatabricks

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

### Databricks無料トライアル

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