sql >> Base de Datos >  >> RDS >> Mysql

Unir tablas en dos bases de datos usando SQLAlchemy

En MySQL bases de datos son sinónimos de esquemas . Donde, por ejemplo, en Postgresql puede consultar entre múltiples esquemas en una base de datos, pero no entre bases de datos (directamente), puede consultar entre múltiples bases de datos en MySQL ya que no hay distinción entre los dos.

En este sentido, una posible solución para su consulta de múltiples bases de datos en MySQL podría ser usar un solo motor, sesión y Base que maneje ambos esquemas y pase el schema argumento de palabra clave a sus tablas, o reflejando ambos esquemas para que estén completamente calificados.

Como no tengo sus datos, hice 2 esquemas (bases de datos MySQL) en un servidor de prueba llamado sopython y 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)

y agregó una tabla en cada uno:

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)

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

Crea el motor sin especificar qué esquema (base de datos) usas por defecto:

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__

La advertencia es algo que no entiendo completamente, y probablemente sea el resultado de la referencia de clave externa entre las 2 tablas que causan un nuevo reflejo de foo, pero no parece causar problemas.

La advertencia es el resultado de la segunda llamada a prepare() recreando y reemplazando las clases por las tablas reflejadas en la primera llamada. La forma de evitar todo eso es reflejar primero las tablas de ambos esquemas usando los metadatos y luego preparar:

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

Después de todo esto, puede consultar la unión de foo y 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>