【问题标题】:Join tables in two databases using SQLAlchemy使用 SQLAlchemy 连接两个数据库中的表
【发布时间】:2017-06-15 09:51:34
【问题描述】:

我正在使用两个 MySQL 数据库。我想在 SQLAlchemy 中将来自 DB 1 的表与来自 DB2 的表连接起来。

我在 sqlalchemy 中创建数据访问层时使用 automap_base 如下...

class DBHandleBase(object):

    def __init__(self, connection_string='mysql+pymysql://root:xxxxxxx@localhost/services', pool_recycle=3600):
            self.Base_ = automap_base()
            self.engine_ = create_engine(connection_string,
                                         pool_recycle = pool_recycle)
            self.Base_.prepare(self.engine_, reflect=True)
            self.session_ = Session(self.engine_)

我的表类就像

class T1D1_Repo():


    def __init__(self, dbHandle):
        # create a cursor
        self.Table_ = dbHandle.Base_.classes.t1
        self.session_ = dbHandle.session_

我正在像这样进行连接,

db1_handle = DB1_Handle()
db2_handle = DB2_Handle()
t1d1_repo = T1D1_Repo(handle)
t1d2_repo = T1D2_Repo(person_handle)

result = t1d1_repo.session_.query(
            t1d1_repo.Table_,
            t1d2_repo.Table_).join(t1d2_repo.Table_, (
                t1d1_repo.Table_.person_id
                == t1d2_repo.Table_.uuid))

我收到这样的错误:

sqlalchemy.exc.ProgrammingError: (pymysql.err.ProgrammingError) (1146, "Table 'db1.t1d2' doesn't exist") [SQL: 'SELECT 

我们在数据库 db1 中创建了表 t1,在数据库 db2 中创建了表 t2。

在 sqlalchemy ORM 中是否可以跨两个数据库表进行连接? 如何实现?

【问题讨论】:

  • 这是不可能的(ish)。例如,您不能针对 db1 触发 SQL 查询并期望它能够查询 db2 中的表。您必须分别查询这些表并在 Python 中“加入”结果。现在,如果您使用支持外部表的数据库,您可以将您的数据库链接在一起,并将另一个数据库中的表作为外部表引入。但我不知道 MySQL 是否支持。
  • 相关:stackoverflow.com/questions/1565993/…stackoverflow.com/questions/9416871/…。之前评论中的“不可能”是一个有点强硬的说法。 MySQL 将允许您在数据库之间进行查询,如果它们驻留在同一台服务器上,那么就会有 FEDERATED 存储引擎。
  • 您的数据库是否在同一台服务器上?
  • 是的,两个数据库都在同一台服务器上

标签: python mysql join sqlalchemy multiple-databases


【解决方案1】:

在 MySQL databases are synonymous with schemas.例如,在 Postgresql 中,您可以在数据库中的多个模式之间进行查询,但不能(直接)在数据库之间进行查询,您可以在 MySQL 中的多个数据库之间进行查询,因为两者之间没有区别。

鉴于此,在 MySQL 中对多数据库查询的一种可能解决方案是使用单个引擎、会话和 Base 来处理您的模式并将 schema keyword argument 传递给您的表,或者反映两种模式以便它们'完全合格。

由于我没有你的数据,我在名为 sopython 和 sopython2 的测试服务器上创建了 2 个模式(MySQL 数据库):

mysql> create database sopython;
Query OK, 1 row affected (0,00 sec)

mysql> create database sopython2;
Query OK, 1 row affected (0,00 sec)

并在每个中添加一个表格:

mysql> use sopython
Database changed
mysql> create table foo (foo_id integer not null auto_increment primary key, name text);
Query OK, 0 rows affected (0,05 sec)

mysql> insert into foo (name) values ('heh');
Query OK, 1 row affected (0,01 sec)

mysql> use sopython2
Database changed
mysql> create table bar (bar_id integer not null auto_increment primary key, foo_id integer, foreign key (foo_id) references `sopython`.`foo` (foo_id)) engine=InnoDB;
Query OK, 0 rows affected (0,07 sec)

mysql> insert into bar (foo_id) values (1);
Query OK, 1 row affected (0,01 sec)

在 Python 中:

In [1]: from sqlalchemy import create_engine

In [2]: from sqlalchemy.orm import sessionmaker

In [3]: from sqlalchemy.ext.automap import automap_base

In [4]: Session = sessionmaker()

In [5]: Base = automap_base()

在不指定默认使用的架构(数据库)的情况下创建引擎:

In [6]: engine = create_engine('mysql+pymysql://user:pass@:6603/')

In [7]: Base.prepare(engine, reflect=True, schema='sopython')

In [8]: Base.prepare(engine, reflect=True, schema='sopython2')
/home/user/SO/lib/python3.5/site-packages/sqlalchemy/ext/declarative/clsregistry.py:120: SAWarning: This declarative base already contains a class with the same class name and module name as sqlalchemy.ext.automap.foo, and will be replaced in the string-lookup table.
  item.__name__

警告是我不完全理解的东西,可能是两个表之间的外键引用导致重新反射 foo 的结果,但它似乎不会造成麻烦。


警告是第二次调用prepare() 重新创建和替换第一次调用中反映的表的类的结果。避免这一切的方法是首先使用元数据反映两个模式中的表,然后准备:

Base.metadata.reflect(engine, schema='sopython')
Base.metadata.reflect(engine, schema='sopython2')
Base.prepare()

在这一切之后你可以查询加入 foo 和 bar:

In [9]: Base.metadata.bind = engine

In [10]: session = Session()

In [11]: query = session.query(Base.classes.bar).\
    ...:     join(Base.classes.foo).\
    ...:     filter(Base.classes.foo.name == 'heh')

In [12]: print(query)
SELECT sopython2.bar.bar_id AS sopython2_bar_bar_id, sopython2.bar.foo_id AS sopython2_bar_foo_id 
FROM sopython2.bar INNER JOIN sopython.foo ON sopython.foo.foo_id = sopython2.bar.foo_id 
WHERE sopython.foo.name = %(name_1)s

In [13]: query.all()
Out[13]: [<sqlalchemy.ext.automap.bar at 0x7ff1ed7eee10>]

In [14]: _[0]
Out[14]: <sqlalchemy.ext.automap.bar at 0x7ff1ed7eee10>

In [15]: _.foo
Out[15]: <sqlalchemy.ext.automap.foo at 0x7ff1ed7f09b0>

【讨论】:

  • 嗨。上面您使用两个不同名称的表构建了一个查询:Base.classes.barBase.classes.foo 您将如何处理两个具有相同表名的数据库?
  • 如果有人在@Jossy 的评论中寻找问题的答案,请参考flask-sqlalchemy.palletsprojects.com/en/2.x/binds
猜你喜欢
  • 2018-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-26
  • 2015-04-20
  • 2015-04-11
  • 2018-06-13
相关资源
最近更新 更多