Databricks上のPySparkからjdbc経由でOracleに接続するパフォーマンスチューニングをしていたら解消までに沼ってだいぶ時間を要したので、備忘として書き残しておきます。
やっていたこと
Databricks上で上記参考にpartitionColumn、lowerBound、upperBoundにタイムスタンプ列を使ってreadしようとしたところ、以下のようなエラーが発生
PySpark
jdbcDF = spark.read \
.format("jdbc") \
.option("url", <URL>) \
.option("dbtable", <table>) \
.option("user", <user>) \
.option("password", <password>) \
.option("numPartitions", spark.default.parallelism) \
.option("partitionColumn", <TIMESTAMP>) \
.option("lowerBound", "2018-01-01 00:00:00") \
.option("upperBound", "2022-12-31 23:59:59") \
.load()
Error
ORA-01843: not a valid month
結論
PySpark
jdbcDF = spark.read \
.format("jdbc") \
.option("url", <URL>) \
.option("dbtable", <table>) \
.option("user", <user>) \
.option("password", <password>) \
.option("numPartitions", spark.default.parallelism) \
.option("partitionColumn", <TIMESTAMP>) \
.option("lowerBound", "2018-01-01 00:00:00") \
.option("upperBound", "2022-12-31 23:59:59") \
.option("sessionInitStatement", "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD' NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'") \
.load()
sessionInitStatementに複数指定する方法が分からなかったり、色々な所に沼ポイントがありました。
(はじめは以下のように書いて通らず苦しんでいた。sessionInitStatementを複数回やると一番最後のもので上書きされる。)
誤った設定値の例
.option("sessionInitStatement", "ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'") \
.option("sessionInitStatement", "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD')
突破口になったのは以下の構文でセッションパラメータを抜いてくることでした。
これで「最後に指定した奴しか効いてない」に気付けました。
PySpark
jdbcDF = spark.read \
.format("jdbc") \
.option("url", <URL>) \
.option("query", "SELECT * FROM nls_session_parameters") \
.option("user", <user>) \
.option("password", <password>) \
.load()
ちなみに以下のようにHH24:MI:SSまでで止めてしまうと、lowerBoundからupperBoundまでの間の計算でほぼ確実に秒に端数が出るので以下のようなエラーになります。
誤った設定値の例
.option("sessionInitStatement", "ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS'") \
Error
ORA-01830: date format picture ends before converting entire input string
同じところでハマる人の目に留まりますよう。