我目前正在为一个新项目评估 SQLAlchemy。在尝试反映现有数据库(postgres、oracle)的表时,我遇到了一些问题。
为了连接到 Oracle 数据库,正在使用以下代码:
from sqlalchemy import create_engine, MetaData, Table, inspect, select
import pandas as pd
import keyring
dbtype = 'Oracle'
dbenv = 'LOCAL'
dbname = 'MYDB'
dbsys = '%s%s' % (dbtype, dbenv)
dbusr = 'myusr'
dbpwd = keyring.get_password(dbsys, dbusr)
dbhost = 'mydbhost'
dbport = 1521
dbconstr = 'oracle+cx_oracle://%s:%s@%s:%s/%s' % (dbusr, dbpwd, dbhost, dbport, dbname)
engine = create_engine(dbconstr)
在对如何组装连接字符串(tnsnames.ora
我的系统上没有)进行了一些尝试和错误之后,我成功地建立了与远程 Oracle 数据库的连接。
在检查数据库并尝试检索可用表的名称时,我收到了一个空列表。
inspector = inspect(engine)
instbls = inspector.get_table_names()
print('instbls: %s' % instbls)
# instbls: []
我实际上期望的是系统表或公共表会被列出。
接下来我尝试反映模式的数据库表SYS
。
md01 = MetaData(bind=engine, schema=['SYS', 'SYSTEM'], reflect=True)
# Warning: sqlalchemy_oracle.py:37: SADeprecationWarning: The MetaData.reflect flag is deprecated and will be removed in a future release. Please use the MetaData.reflect() method.
print('type(md01): %s' % type(md01))
# type(md01): <class 'sqlalchemy.sql.schema.MetaData'>
md01tbls = md01.tables
print('md01tbls: %s' % md01tbls)
# md01tbls: immutabledict({})
所以我相应地修改了代码以消除警告
md02 = MetaData(bind=engine, schema=['SYS', 'SYSTEM']).reflect()
print('type(md02): %s' % type(md02))
# type(md02): <class 'NoneType'>
md02tbls = md02.tables
# Exception: Traceback (most recent call last):
# ...
# File "sqlalchemy_oracle.py", line 44, in <module>
# AttributeError: 'NoneType' object has no attribute 'tables'
print('md02tbls: %s' % md02tbls)
没有警告,而是一个例外。:(
在对网络进行了一些研究之后,似乎需要进行一些进一步的代码修改。
md03 = MetaData().reflect(bind=engine, schema=['SYS', 'SYSTEM'])
# Exception: Traceback (most recent call last):
# ...
# File "/site-packages/sqlalchemy/engine/default.py", line 637, in denormalize_name
# name_lower = name.lower()
# AttributeError: 'list' object has no attribute 'lower'
print('type(md03): %s' % type(md03))
md03tbls = md03.tables
print('md03tbls: %s' % md03tbls)
甚至不如之前的尝试成功。将列出的模式名称转换为小写字母会导致相同的结果。
所以也许reflect
只能处理strings
而没有lists
?
md05a = MetaData().reflect(bind=engine, schema='sys')
print('type(md05a): %s' % type(md05a))
# type(md05a): <class 'NoneType'>
md05b = MetaData().reflect(bind=engine, schema='system')
print('type(md05b): %s' % type(md05b))
# type(md05b): <class 'NoneType'>
md05atbls = md05a.tables
# Exception: Traceback (most recent call last):
# ...
# File "sqlalchemy_oracle.py", line 44, in <module>
# AttributeError: 'NoneType' object has no attribute 'tables'
print('md05atbls: %s' % md05atbls)
md05btbls = md05b.tables
print('md05btbls: %s' % md05btbls)
那里也没有运气!
也许无法反映系统表?所以让我们尝试用户特定的对象。
md06 = MetaData().reflect(bind=engine, schema='MYUSR')
print('type(md06): %s' % type(md06))
# type(md06): <class 'NoneType'>
md06tbls = md06.tables
# Exception: Traceback (most recent call last):
# ...
# File "sqlalchemy_oracle.py", line 44, in <module>
# AttributeError: 'NoneType' object has no attribute 'tables'
print('md06tbls: %s' % md06tbls)
再次是已知的例外。
回到不推荐使用的调用版本。
md07 = MetaData(bind=engine, schema=['MYUSR', 'MYUSR2'], reflect=True)
print('type(md07): %s' % type(md07))
# type(md07): <class 'sqlalchemy.sql.schema.MetaData'>
md07tbls = md07.tables
print('md07tbls: %s' % md07tbls)
# md07tbls: immutabledict({})
也不例外,但也没有表,尽管在模式MYUSR
和MYUSR2
.
指定架构时拆分list
为。strings
md08a = MetaData(bind=engine, schema='MYUSR', reflect=True)
# Warning: sqlalchemy_oracle.py:37: SADeprecationWarning: The MetaData.reflect flag is deprecated and will be removed in a future release. Please use the MetaData.reflect() method.
print('type(md08a): %s' % type(md08a))
# ype(md08a): <class 'sqlalchemy.sql.schema.MetaData'>
md08b = MetaData(bind=engine, schema='MYUSR2', reflect=True)
# Warning: sqlalchemy_oracle.py:40: SADeprecationWarning: The MetaData.reflect flag is deprecated and will be removed in a future release. Please use the MetaData.reflect() method.
print('type(md08b): %s' % type(md08b))
# type(md08b): <class 'sqlalchemy.sql.schema.MetaData'>
md08atbls = md08a.tables
print('md08atbls: %s' % md08atbls)
# md08atbls: immutabledict({'MYUSR.table01': Table(...)})
md08btbls = md08b.tables
print('md08btbls: %s' % md08btbls)
# md08btbls: immutabledict({'MYUSR2.table01': Table(...)})
所以这次尝试最终产生了预期的结果。:)
从这一切看来,
- 无法反映系统/公共表(数据库对象);
- 只能反映用户特定的表;
- 只有不推荐使用的反射版本有效;
- 在 Oracle 和 PostgreSQL 数据库中都可以观察到这种行为。
有什么我忽略的吗?我有什么误解?做错了,与实际意图不同?
我的环境包括
- Ubuntu linux 16.04LTS;
- 蟒蛇3.8;
- SQLAlchemy 1.3.16;
- cx_Oracle 7.3.0;
- psycopg2 2.8.5;
- 本地 Oracle 18c Instant 客户端;
- 远程 Oracle 19c 数据库;
- 本地 PostgreSQL 9.5 数据库。