自分のキャリアをあれこれ考えながら、Pythonで様々なデータを分析していくブログです

pythonからOracleを操作する「cx_Oracle」が「python-oracledb」になったのでさっそく使ってみた

Python
Python

今までPythonからOracleデータベースにアクセスするライブラリとして、「cx_Oracle」を利用していました。

しかし、cx_Oracleは「python-oracledb」という名称になったようです。

cx_Oracle has a major new release under a new name and homepage python-oracledb.
引用: https://oracle.github.io/python-cx_Oracle/

本記事ではpython-oracledbをインストールしてOracleデータベースからデータを取得するところまでやってみようと思います。

勉強用のOracleデータベースの準備やcx_Oracleの情報をお探しの方は下記記事をご覧ください。

Oracle XE環境の準備はこちら

cx_Oracleのインストールと使い方はこちら

スポンサーリンク

python-oracledbとは何か

PythonとオラクルDBを繋ぐためのモジュールでcx_Oracleが名称変更して生まれ変わったドライバーです。

The python-oracledb driver is a Python programming language extension module allowing Python programs to connect to Oracle Database. Python-oracledb is the new name for Oracle's popular cx_Oracle driver. 引用: https://python-oracledb.readthedocs.io/en/latest/user_guide/installation.html

cx_Oracleでは必要であったOracle Instant Clientをインストールしなくても、ThinモードでOracle 12c以降であればデータベースにアクセスすることが出来るようです。

Thin mode: By default, python-oracledb is a 'Thin' driver which connects directly to Oracle Database. This mode does not need Oracle Client libraries.引用: https://python-oracledb.readthedocs.io/en/latest/user_guide/installation.html

もしOracle11.2を使いたい場合やThinモードでサポートされていない機能を使いたい場合は、Oracle Instant ClientをインストールしてThickモードで利用した方が良さそうです。

例えばコネクション文字列で(ENABLE=BROKEN)はThinモードでは使えないといった制約があるようです。

The ENABLE=BROKEN connect descriptor option is not supported in python-oracledb Thin mode. Use expire_time instead.
引用: https://python-oracledb.readthedocs.io/en/latest/user_guide/appendix_b.html#connection-strings

私だったら本番環境で運用する場合はOracle Instant ClientをインストールしてThickモード、それ以外の勉強や開発時の確認などのみで使いたい場合はThinモードという風に使い分けると思います。(今はまだcx_Oracle使って本番運用してる)

その他にもcx_Oracleから変わった点として下記があるようです。

・パラメータのカプセル化が出来るようになった。

カプセル化例
params = oracledb.ConnectParams(host="localhost", port=1521, service_name="XE")
conn = oracledb.connect(user="SYSTEM", password="hinomaruc", params=params)

・encoding、nencoding、threadedのパラメータが不要になった(deprecated and ignoredされました)

cx_Oracleとpython-oracledbの違いに関しては「Appendix C: The python-oracledb and cx_Oracle Drivers」に詳しく記載されています。

ちなみにcx_Oracleからpython-oracledbにアップデートする手順は「steps-to-upgrade-to-python-oracledb」に手順があります。

cx_Oracleからアップデートする場合だとOracle Instant Clientがインストールされていると思うので、python-oracledbはThickモードで利用するのが良さそうですね。

スポンサーリンク

python-oracledbの利用準備

まずはOracle環境のセットアップからやります。

dockerでoracleデータベースXEを簡単に起動できますので、データベースの立ち上げからデータ挿入まで事前にやっておきます。

その次にpython-oracledbのインストールとThinモードでアクセスし挿入したデータを取得しpandasへ格納するところまで確認が出来たらと考えています。

dockerでoracleデータベースXEの準備

こちらの記事でOracleデータベースXEをインストールする3つの方法をまとめていますが、今回は簡単に構築できるOracle Cloud Infrastructure Container Registryを使います。

oracleデータベースXE21.3.0のイメージをpull
docker pull container-registry.oracle.com/database/express:21.3.0-xe
Out[0]
21.3.0-xe: Pulling from database/express
63183c9b4598: Pull complete 
fe297bb4960b: Pull complete 
31dd4858f370: Pull complete 
4f4fb700ef54: Pull complete 
b4584ceb9e7a: Pull complete 
Digest: sha256:016d1a2becd9c9b9bfb683eebf3aa092527fe1354ace5b23691e75759f301bed
Status: Downloaded newer image for container-registry.oracle.com/database/express:21.3.0-xe
container-registry.oracle.com/database/express:21.3.0-xe
コンテナを起動
docker run -it -d --name oracledb \
-p 1521:1521 -p 5500:5500 \
-e ORACLE_PWD=hinomaruc \
container-registry.oracle.com/database/express:21.3.0-xe

ORACLE_PWDでデータベースのパスワードを指定します。

コンテナのログを確認する(デタッチでrunした場合)
docker logs -f --tail=100 oracledb
Out[0]
Starting Oracle Net Listener.
Oracle Net Listener started.
Starting Oracle Database instance XE.
Oracle Database instance XE started.

The Oracle base remains unchanged with value /opt/oracle

SQL*Plus: Release 21.0.0.0.0 - Production on Thu Dec 1 12:05:07 2022
Version 21.3.0.0.0

Copyright (c) 1982, 2021, Oracle.  All rights reserved.

Connected to:
Oracle Database 21c Express Edition Release 21.0.0.0.0 - Production
Version 21.3.0.0.0

SQL> 
User altered.

SQL> 
User altered.

SQL> 
Session altered.

SQL> 
User altered.

SQL> Disconnected from Oracle Database 21c Express Edition Release 21.0.0.0.0 - Production
Version 21.3.0.0.0
The Oracle base remains unchanged with value /opt/oracle
#########################
DATABASE IS READY TO USE!
#########################
The following output is now a tail of the alert.log:
XEPDB1(3):Opening pdb with Resource Manager plan: DEFAULT_PLAN
Pluggable database XEPDB1 opened read write
・・・省略・・・
Resize operation completed for file# 3, fname /opt/oracle/oradata/XE/sysaux01.dbf, old size 573440K, new size 593920K

少し時間はかかりますが、Oracleデータベースが使える状態になったようです。

コンテナにアクセスし、sqlplusを起動
# ユーザ名/パスワード@XE
docker exec -it oracledb sqlplus system/hinomaruc@XE
Out[0]
SQL*Plus: Release 21.0.0.0.0 - Production on Thu Dec 8 13:16:47 2022
Version 21.3.0.0.0

Copyright (c) 1982, 2021, Oracle.  All rights reserved.

Last Successful login time: Thu Dec 08 2022 13:15:53 +00:00

Connected to:
Oracle Database 21c Express Edition Release 21.0.0.0.0 - Production
Version 21.3.0.0.0

SQL> select 1 from dual;

     1
----------
     1

無事sqlplusでデータベースに繋ぐことができました。

Oracleデータベースにテスト用のテーブルを作成しデータを挿入する

(オプション) docker execで特定のsqlを流す方法
# 参考: https://stackoverflow.com/questions/34779894/executing-sql-scripts-on-docker-container
docker exec -it oracledb /bin/bash -c 'echo "select 1 from dual;" | sqlplus -S system/hinomaruc@XE'
Out[0]
     1
----------
     1

コンテナの中に入らなくてもホスト側である程度作業は出来るかも知れません。

しかし私はインタラクティブにクエリ発行したいので、コンテナ内に入ってからテーブル作成とデータ挿入をしてみます。

テーブル作成
docker exec -it oracledb sqlplus system/hinomaruc@XE
SQL> CREATE TABLE SAMPLE_TABLE (FRUIT_NAME VARCHAR(255),PROD_ID INT);
SQL> INSERT INTO SAMPLE_TABLE (FRUIT_NAME, PROD_ID) VALUES ('apple', 0);
SQL> INSERT INTO SAMPLE_TABLE (FRUIT_NAME, PROD_ID) VALUES ('apple', 1);
SQL> INSERT INTO SAMPLE_TABLE (FRUIT_NAME, PROD_ID) VALUES ('orange', 2);
SQL> INSERT INTO SAMPLE_TABLE (FRUIT_NAME, PROD_ID) VALUES ('orange', 3);
SQL> INSERT INTO SAMPLE_TABLE (FRUIT_NAME, PROD_ID) VALUES ('banana', 4);
SQL> INSERT INTO SAMPLE_TABLE (FRUIT_NAME, PROD_ID) VALUES ('banana', 5);
SQL> INSERT INTO SAMPLE_TABLE (FRUIT_NAME, PROD_ID) VALUES ('peach', 6);
SQL> commit;
Out[0]
Table created.
1 row created.
1 row created.
1 row created.
1 row created.
1 row created.
1 row created.
1 row created.
Commit complete.
データが入ったかどうか確認
docker exec -it oracledb /bin/bash -c 'echo -e "SET pagesize 0; \n select FRUIT_NAME,PROD_ID from SAMPLE_TABLE;" | sqlplus -S system/hinomaruc@XE'
Out[0]
apple
     0

apple
     1

orange
     2

orange
     3

banana
     4

banana
     5

peach
     6

きちんとデータが登録されているようです。

python-oracledbのインストール

インストールする前の確認事項ですが、Pythonのバージョンに指定があります。

22年12月時点ですとLinuxのみPython3.6にも対応していましたが今はどのプラットフォームもPython 3.7以上に統一されたようです。

23年10月17日現在、下記Pythonのバージョンに対応しているようです。※ 利用前に引用元のリンクから最新の情報をご確認ください。

To use python-oracledb, you need:
Python 3.7, 3.8, 3.9, 3.10, 3.11 or 3.12
引用: https://python-oracledb.readthedocs.io/en/latest/user_guide/installation.html#installation-requirements

python-oracledbをpipでインストールする
python3 -m pip install oracledb --upgrade
Out[0]
・・・省略・・・
      clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -iwithsysroot/System/Library/Frameworks/System.framework/PrivateHeaders -iwithsysroot/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/Headers -arch arm64 -arch x86_64 -I/Users/hinomaruc/Desktop/blog/venv-oracle/include -I/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/Headers -c src/oracledb/base_impl.c -o build/temp.macosx-10.14.6-x86_64-cpython-38/src/oracledb/base_impl.o -mmacosx-version-min=10.9
      src/oracledb/base_impl.c:6:10: fatal error: 'Python.h' file not found
      #include "Python.h"
               ^~~~~~~~~~
      1 error generated.
      /private/var/folders/n0/ft3172td6v70d6klgt216_3r0000gn/T/pip-build-env-b_f28qgr/overlay/lib/python3.8/site-packages/setuptools/config/expand.py:144: UserWarning: File '/private/var/folders/n0/ft3172td6v70d6klgt216_3r0000gn/T/pip-install-e387z73n/oracledb_7b6209e3673340219d47bc303da3c31c/README.md' cannot be found
        warnings.warn(f"File {path!r} cannot be found")
      error: command '/usr/bin/clang' failed with exit code 1
      [end of output]

  note: This error originates from a subprocess, and is likely not a problem with pip.
  ERROR: Failed building wheel for oracledb
Failed to build oracledb
ERROR: Could not build wheels for oracledb, which is required to install pyproject.toml-based projects

エラーになってしまいました。Python.hが見つからないようです。

どうやらpython-develかそれに準ずるものが必要なようです。Linuxであればapt-get install python-develなどで解決しそうですが、Macの場合はbrewでインストールしたPythonを使うとPython.hが含まれているようです。

システムで最初からインストールされているPython3を使わず、brew install python@3.8などでインストールしたPythonを使おうと思います。

Compiling python-oracledb requires the Python.h header file. If you are using the default python package, this file is in the python-devel package or equivalent.
引用: https://python-oracledb.readthedocs.io/en/latest/user_guide/installation.html#install-python-oracledb

If you install Python using brew, the relevant headers are already installed for you.
引用: https://stackoverflow.com/questions/32578106/how-to-install-python-devel-in-mac-os

brewでpython3.8をインストールし使えるようにする
brew install python@3.8
# リンクを貼る
ln -s /usr/local/opt/python@3.8/bin/python3.8 /usr/local/bin/python3.8
# brewでインストールしたpython3.8で仮想環境を作成
python3.8 -m venv venv-oracle
source venv-oracle/bin/activate
python3 -m pip install --upgrade pip
# メインのjupyter環境からvenv-oracleのカーネルを参照できるようにする
python3 -m pip install ipykernel
python3 -m ipykernel install --user --name venv-oracle --display-name "venv-oracle"
もう一回python-oracledbをインストールしてみる
python3 -m pip install oracledb --upgrade
Out[0]
Collecting oracledb
・・・省略・・・
Successfully built oracledb
Installing collected packages: pycparser, cffi, cryptography, oracledb
Successfully installed cffi-1.15.1 cryptography-38.0.4 oracledb-1.2.1 pycparser-2.21

今度は無事インストールが完了しました。

スポンサーリンク

python-oracledbを使ってみる

[Mac] Pythonからcx_OracleでOracleデータベースを操作してみる」の記事と同じようなコードでアクセス出来ました。

読み込むライブラリ名がcx_Oracleからoracledbに変更になるだけで、基本的に同じような仕組みでデータの取得とアウトプットが出来るようです。

データのフェッチと出力

# 参考:https://python-oracledb.readthedocs.io/en/latest/user_guide/connection_handling.html
import oracledb
#Thickモードで使いたい時はインスタントクライアントを読み込む
#oracledb.init_oracle_client(lib_dir=r"/Users/hinomaruc/Desktop/blog/instantclient_19_8")
connection = oracledb.connect(user="system", password="hinomaruc", dsn="localhost/XE")
cur = connection.cursor()
cur.execute("""SELECT TABLE_NAME FROM USER_TABLES""")
# 1行だけフェッチする
cur.fetchone()
Out[0]
('LOGMNR_SESSION_EVOLVE$',)
# 残り全行フェッチする
rows = cur.fetchall()
for r in rows:
    print(r)
Out[0]
('LOGMNR_GLOBAL$',)
・・・省略・・・
('SAMPLE_TABLE',)

きちんと取得出来ているようです。

# カーソルとコネクションをクローズする
cur.close()
connection.close()

python-oracledbを使ってpandasにデータを読み込む

こちらも「Oracleのデータをpandasに読み込んでみる」でcx_Oracleを使っている処理とあまり変わりないです。

python-oracledbのコネクションを作成しpandasに読み込む

python-oracledbのコネクションを作成しpandasに読み込む
import oracledb
import pandas as pd
connection = oracledb.connect(user="system", password="hinomaruc", dsn="localhost/XE")

query = """SELECT * FROM SAMPLE_TABLE"""
df = pd.read_sql(query, con=connection)
connection.close()
df 
Out[0]

/var/folders/n0/ft3172td6v70d6klgt216_3r0000gn/T/ipykernel_1886/2190498718.py:6: UserWarning: pandas only supports SQLAlchemy connectable (engine/connection) or database string URI or sqlite3 DBAPI2 connection. Other DBAPI2 objects are not tested. Please consider using SQLAlchemy.df = pd.read_sql(query, con=connection)

FRUIT_NAME PROD_ID
0 apple 0
1 apple 1
2 orange 2
3 orange 3
4 banana 4
5 banana 5
6 peach 6

OracleDBのデータは取得出来ているようですが、warningがでてしまいました。pandas only supports SQLAlchemy connectable (engine/connection) ... connectionとありますので、SQLAlchemyのエンジンを作成して繋げてみようと思います。

22年12月現在、最新版であるsqlAlchemy1.4はcx_Oracleにしか対応していないようです。

python-oracledbをネィティブサポートしていない場合はpython-oracledbをcx_Oracleとして使う暫定処理があるようなので試してみます。

python-oracledb can be used in SQLAlchemy, Django, Pandas, and other frameworks and Object-relational Mappers (ORMs). Until they add native support, you can override the use of cx_Oracle with a few lines of code.
引用: https://python-oracledb.readthedocs.io/en/latest/user_guide/appendix_c.html#things-to-know-before-the-upgrade

SQLAlchemy1.4はpython-oracledbに対応していませんが、ベータ版である2.0betaは対応しているようです。ベータ版でも問題ない方は2.0beta以上を使うといいかも知れません。

SQLAlchemyのエンジンを作成しpandasに読み込む

SQLAlchemyのエンジンを作成しpandasに読み込む
import sys
import oracledb
import pandas as pd

# python-oracledbをcx_Oracleとして利用する暫定処理
oracledb.version = "8.3.0"
sys.modules["cx_Oracle"] = oracledb
import cx_Oracle

# importしているcx_Oracleが本当のcx_Oracleなのか、cx_Oracleとして使ってるoracledbなのか判別
if cx_Oracle.__name__ == 'cx_Oracle':
       print('cx_Oracle')
else:
       print('oracledb')

# https://docs.sqlalchemy.org/en/14/dialects/oracle.html
from sqlalchemy import create_engine
engine = create_engine("oracle+cx_oracle://SYSTEM:hinomaruc@localhost/XE",max_identifier_length=30)
query = """SELECT * FROM SAMPLE_TABLE"""
df = pd.read_sql(query, con=engine)
engine.close()
df 
Out[0]

fruit_name prod_id
0 apple 0
1 apple 1
2 orange 2
3 orange 3
4 banana 4
5 banana 5
6 peach 6

今度はwarningが出ませんでした。

スポンサーリンク

まとめ

oracleデータベースの構築からpython-oracledbを使ってpythonからオラクルのテーブルのデータを取得するところまで実施することが出来ました。python-oracledbのThinモードを利用することによって、Oracle Instant Clientなしで利用すること出来るので手順が簡単になったのかなと思います。

これからPythonとOracleデータベース間のやり取りをしたい方はpython-oracledbをインストールすることをおすすめします。

スポンサーリンク

ライブラリのバージョン

import pandas as pd
import oracledb
import sqlalchemy

print("pandas",pd.__version__)
print("python-oracledb",oracledb.__version__)
print("SQLAlchemy",sqlalchemy.__version__)
Out[0]
pandas 1.5.2
python-oracledb 1.2.1
SQLAlchemy 1.4.45
タイトルとURLをコピーしました