Объединение таблиц в двух базах данных с помощью SQLAlchemy


Я работаю с двумя базами данных MySQL. Я хочу соединить таблицу из DB1 с таблицей из DB2 в SQLAlchemy.

Я использую automap_base при создании слоя доступа к данным в sqlalchemy следующим образом...

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 

Мы создали таблицу t1 в базе данных db1 и таблицу t2 в базе данных db2.

Do join возможно через два таблица баз данных в sqlalchemy ORM? Как этого добиться?

1 3

1 ответ:

В MySQLбазы данных синонимичны схемам . Если, например, в Postgresql вы можете выполнять запросы между несколькими схемами в базе данных, но не между базами данных (напрямую), вы можете выполнять запросы между несколькими базами данных в MySQL, поскольку нет никакого различия между ними.

В этом свете возможным решением для вашего запроса к нескольким базам данных в MySQL может быть использование одного движка, сеанса и базы обработки как ваших схем, так и передачи schema аргумент ключевого слова to ваши таблицы, или отражающие обе схемы, чтобы они были полностью квалифицированы.

Поскольку у меня нет ваших данных, я сделал 2 схемы (базы данных MySQL) на тестовом сервере под названием sopython и sopython2:
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__

Предупреждение-это то, что я не совсем понимаю, и, вероятно, является результатом ссылки на внешний ключ между 2-мя столами возникает переотражение фу, но это, кажется, не вызывает неприятностей.


Предупреждение является результатом второго вызова 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>