Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Junte tabelas em dois bancos de dados usando SQLAlchemy


No MySQL, bancos de dados são sinônimos de esquemas . Onde, por exemplo, no Postgresql você pode consultar entre vários esquemas em um banco de dados, mas não entre bancos de dados (diretamente), você pode consultar entre vários bancos de dados no MySQL, pois não há distinção entre os dois.

Sob essa luz, uma possível solução para sua consulta de vários bancos de dados no MySQL poderia ser usar um único mecanismo, sessão e Base manipulando seus esquemas e passando o schema argumento de palavra-chave às suas tabelas ou refletindo ambos os esquemas para que sejam totalmente qualificados.

Como não tenho seus dados, fiz 2 esquemas (bancos de dados MySQL) em um servidor de teste chamado sopython e 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)

e adicionei uma tabela em cada:
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)

Em 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()

Crie o mecanismo sem especificar qual esquema (banco de dados) você usa por padrão:
In [6]: engine = create_engine('mysql+pymysql://user:[email protected]: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__

O aviso é algo que eu não entendo completamente e é provavelmente o resultado da referência de chave estrangeira entre as 2 tabelas causando re-reflexão de foo, mas não parece causar problemas.

O aviso é o resultado da segunda chamada para prepare() recriando e substituindo as classes das tabelas refletidas na primeira chamada. A maneira de evitar tudo isso é primeiro refletir as tabelas de ambos os esquemas usando os metadados e depois preparar:
Base.metadata.reflect(engine, schema='sopython')
Base.metadata.reflect(engine, schema='sopython2')
Base.prepare()

Depois de tudo isso, você pode consultar a junção de foo e 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>