Mysql
 sql >> Base de données >  >> RDS >> Mysql

Joindre des tables dans deux bases de données à l'aide de SQLAlchemy

Dans MySQL les bases de données sont synonymes de schémas . Là où, par exemple, dans Postgresql, vous pouvez interroger entre plusieurs schémas dans une base de données, mais pas entre les bases de données (directement), vous pouvez interroger entre plusieurs bases de données dans MySQL car il n'y a pas de distinction entre les deux.

Dans cette optique, une solution possible à votre requête multi-base de données dans MySQL pourrait être d'utiliser un seul moteur, session et base gérant à la fois vos schémas et en passant le schema argument de mot-clé à vos tables, ou reflétant les deux schémas afin qu'ils soient pleinement qualifiés.

Comme je n'ai pas vos données, j'ai fait 2 schémas (bases de données MySQL) sur un serveur de test appelé sopython et 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)

et ajouté un tableau dans chacun :

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

Créez le moteur sans préciser quel schéma (base de données) vous utilisez par défaut :

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__

L'avertissement est quelque chose que je ne comprends pas entièrement, et est probablement le résultat de la référence de clé étrangère entre les 2 tables provoquant la re-réflexion de foo, mais cela ne semble pas causer de problème.

L'avertissement est le résultat du deuxième appel à prepare() recréer et remplacer les classes pour les tables reflétées dans le premier appel. Le moyen d'éviter tout cela est de d'abord refléter les tables des deux schémas à l'aide des métadonnées, puis de préparer :

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

Après tout cela, vous pouvez interroger foo et 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>