2012-05-23 8 views
1

J'essaie d'adapter une partie d'une application MySQLdb à sqlalchemy dans une base déclarative. Je ne fais que commencer avec sqlalchemy.sqlalchemy: union requête quelques colonnes de plusieurs tables avec condition

Les tableaux anciens sont définis quelque chose comme:

student: id_number*, semester*, stateid, condition, ... 
choice: id_number*, semester*, choice_id, school, program, ...

Nous avons 3 tables pour chacun d'eux (student_tmp, student_year, student_summer, choice_tmp, choice_year, choice_summer), de sorte que chaque paire (_tmp, _year, _summer) contient des informations pour un moment spécifique.

select * 
from `student_tmp` 
    inner join `choice_tmp` using (`id_number`, `semester`)

Mon problème est l'information qui est important pour moi est d'obtenir l'équivalent de ce qui suit select:

SELECT t.* 
FROM (
     (
      SELECT st.*, ct.* 
      FROM `student_tmp` AS st 
       INNER JOIN `choice_tmp` as ct USING (`id_number`, `semester`) 
      WHERE (ct.`choice_id` = IF(right(ct.`semester`, 1)='1', '3', '4')) 
       AND (st.`condition` = 'A') 
     ) UNION (
      SELECT sy.*, cy.* 
      FROM `student_year` AS sy 
       INNER JOIN `choice_year` as cy USING (`id_number`, `semester`) 
      WHERE (cy.`choice_id` = 4) 
       AND (sy.`condition` = 'A') 
     ) UNION (
      SELECT ss.*, cs.* 
      FROM `student_summer` AS ss 
       INNER JOIN `choice_summer` as cs USING (`id_number`, `semester`) 
      WHERE (cs.`choice_id` = 3) 
       AND (ss.`condition` = 'A') 
     ) 
    ) as t

* utilisé pour réduire la sélection, mais je suis en fait interroger seulement environ 7 colonnes sur les 50 disponibles.

Cette information est utilisée dans beaucoup de saveurs ... "Est-ce que j'ai des nouveaux étudiants, est-ce que j'ai toujours tous les élèves d'une date donnée, quels étudiants sont inscrits après la date donnée? L'instruction select doit être enregistrée dans une autre base de données.

Serait-il possible pour moi d'y parvenir avec une classe de vue unique? L'information est en lecture seule, je n'ai donc pas besoin de pouvoir modifier/créer/supprimer. Ou dois-je déclarer une classe pour chaque table (se terminant avec 6 classes) et chaque fois que j'ai besoin d'interroger, je dois me rappeler de filtrer?

Merci pour les pointeurs.

EDIT: Je n'ai pas d'accès de modification à la base de données (je ne peux pas créer une vue). Les deux bases de données peuvent ne pas être sur le même serveur (donc je ne peux pas créer une vue sur ma deuxième DB).

Mon souci est d'éviter l'analyse complète de la table avant de filtrer sur condition et choice_id.

EDIT 2: Je l'ai mis en place des classes déclaratives comme ceci:

class BaseStudent(object): 
    id_number = sqlalchemy.Column(sqlalchemy.String(7), primary_key=True) 
    semester = sqlalchemy.Column(sqlalchemy.String(5), primary_key=True) 
    unique_id_number = sqlalchemy.Column(sqlalchemy.String(7)) 
    stateid = sqlalchemy.Column(sqlalchemy.String(12)) 
    condition = sqlalchemy.Column(sqlalchemy.String(3)) 

class Student(BaseStudent, Base): 
    __tablename__ = 'student' 

    choices = orm.relationship('Choice', backref='student') 

#class StudentYear(BaseStudent, Base):... 
#class StudentSummer(BaseStudent, Base):... 

class BaseChoice(object): 
    id_number = sqlalchemy.Column(sqlalchemy.String(7), primary_key=True) 
    semester = sqlalchemy.Column(sqlalchemy.String(5), primary_key=True) 
    choice_id = sqlalchemy.Column(sqlalchemy.String(1)) 
    school = sqlalchemy.Column(sqlalchemy.String(2)) 
    program = sqlalchemy.Column(sqlalchemy.String(5)) 


class Choice(BaseChoice, Base): 
    __tablename__ = 'choice' 

    __table_args__ = (
      sqlalchemy.ForeignKeyConstraint(['id_number', 'semester',], 
       [Student.id_number, Student.semester,]), 
      ) 

#class ChoiceYear(BaseChoice, Base): ... 
#class ChoiceSummer(BaseChoice, Base): ... 

Maintenant, la requête qui me donne SQL correct pour un jeu de table est:

q = session.query(StudentYear, ChoiceYear) \ 
      .select_from(StudentYear) \ 
      .join(ChoiceYear) \ 
      .filter(StudentYear.condition=='A') \ 
      .filter(ChoiceYear.choice_id=='4')

mais il émet une exception ...

"Could not locate column in row for column '%s'" % key) 
sqlalchemy.exc.NoSuchColumnError: "Could not locate column in row for column '*'"

Comment utiliser cette requête me créer une classe que je peux utiliser?

+0

Quel est le modèle 'Social' référencé dans FK de' Choice'? – van

+0

Désolé, c'est censé être étudiant, mis à jour. – Danosaure

Répondre

3

Si vous pouvez créer cette vue dans la base de données, il vous suffit de mapper la vue comme s'il s'agissait d'une table. Voir Reflecting Views.

# DB VIEW 
CREATE VIEW my_view AS -- @todo: your select statements here 

# SA 
my_view = Table('my_view', metadata, autoload=True) 
# define view object 
class ViewObject(object): 
    def __repr__(self): 
     return "ViewObject %s" % str((self.id_number, self.semester,)) 
# map the view to the object 
view_mapper = mapper(ViewObject, my_view) 

# query the view 
q = session.query(ViewObject) 
for _ in q: 
    print _ 

Si vous ne pouvez pas créer un VIEW au niveau de la base de données, vous pouvez créer une carte sélectionnable et le ViewObject lui.Le code ci-dessous devrait vous donner l'idée:

student_tmp = Table('student_tmp', metadata, autoload=True) 
choice_tmp = Table('choice_tmp', metadata, autoload=True) 
# your SELECT part with the columns you need 
qry = select([student_tmp.c.id_number, student_tmp.c.semester, student_tmp.stateid, choice_tmp.school]) 
# your INNER JOIN condition 
qry = qry.where(student_tmp.c.id_number == choice_tmp.c.id_number).where(student_tmp.c.semester == choice_tmp.c.semester) 
# other WHERE clauses 
qry = qry.where(student_tmp.c.condition == 'A') 

Vous pouvez créer 3 requêtes comme celui-ci, puis les combiner avec union_all et utiliser la requête résultant du mappeur:

view_mapper = mapper(ViewObject, my_combined_qry) 

Dans les deux cas, vous avez pour vous assurer qu'une clé primaire est correctement définie dans la vue et que vous devrez peut-être override afficher automatiquement la vue et spécifier explicitement la clé primaire (voir le lien ci-dessus). Sinon, vous recevrez une erreur ou vous risquez de ne pas obtenir les résultats corrects de la requête.


Réponse à EDIT 2:

qry = (session.query(StudentYear, ChoiceYear). 
     select_from(StudentYear). 
     join(ChoiceYear). 
     filter(StudentYear.condition == 'A'). 
     filter(ChoiceYear.choice_id == '4') 
     ) 

Le résultat sera des paires de tuples: (Student, Choice).
Mais si vous voulez créer une nouvelle classe mappée pour la requête, vous pouvez créer un sélectionnable comme l'exemple ci-dessus:

student_tmp = StudentTmp.__table__ 
choice_tmp = ChoiceTmp.__table__ 
.... (see sample code above) 
+0

Merci, mais je n'ai pas d'accès de modification à la base de données, seul accès en lecture seule à cette base de données. Je pense à créer la vue sur ma base de données, mais cela peut ne pas fonctionner si les bases de données sont sur des serveurs différents. – Danosaure

+0

@Danosaure: édité la réponse avec la possibilité de la mapper quand on ne peut pas créer d'objets DB. – van

+0

Est-ce que cela peut être fait base déclarative? J'ai configuré mes cours. J'ai un problème pour définir 'condition == 'A'' dans mon' primaryjoin' de 'orm.relationship'. – Danosaure

1

C'est de montrer ce que je fini par faire, tout commentaire accueilli.


class JoinedYear(Base): 
    __table__ = sqlalchemy.select(
      [ 
       StudentYear.id_number, 
       StudentYear.semester, 
       StudentYear.stateid, 
       ChoiceYear.school, 
       ChoiceYear.program, 
       ], 
       from_obj=StudentYear.__table__.join(ChoiceYear.__table__), 
       ) \ 
       .where(StudentYear.condition == 'A') \ 
       .where(ChoiceYear.choice_id == '4') \ 
       .alias('YearView')

et je développerai à partir de là ...

Merci @van

Questions connexes