2012-05-23 12 views
1

Я пытаюсь адаптировать некоторую часть приложения MySQLdb к sqlalchemy в декларативной базе. Я только начинаю с sqlalchemy.sqlalchemy: union query несколько столбцов из нескольких таблиц с условием

наследие таблиц определены что-то вроде:

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

У нас есть 3 таблицы для каждого из них (student_tmp, student_year, student_summer, choice_tmp, choice_year, choice_summer), так что каждая пара (_tmp, _year, _summer) содержит информацию для определенного момента.

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

Моя проблема заключается в том, что информация важна для меня, чтобы получить эквивалент следующее: выберите

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

* используется для укоротить выбора, но я на самом деле запрашивая только о 7 столбцов из 50 доступных.

Эта информация используется во многих вариантах ... «У меня есть новые ученики? У меня все еще есть ученики с определенной даты? Какие ученики подписаны после указанной даты? И т. Д.». Результат этого select должен быть сохранен в другой базе данных.

Можно ли достичь этого с помощью одного класса, подобного виду? Информация доступна только для чтения, поэтому мне не нужно изменять/создавать/делить. Или мне нужно объявлять класс для каждой таблицы (заканчивая 6 классами), и каждый раз, когда мне нужно запрашивать, я должен помнить, что нужно фильтровать?

Спасибо за указатели.

EDIT: У меня нет доступа к модификации базы данных (я не могу создать представление). Обе базы данных могут быть не на одном сервере (поэтому я не могу создать представление на моем втором БД).

Моя забота состоит в том, чтобы избежать полного сканирования таблицы перед фильтрацией на condition и choice_id.

EDIT 2: Я настроил декларативных классы, как это:

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): ... 

Теперь запрос, который дает мне правильный SQL для одного набора таблицы:

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

но он выдает исключение ...

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

Как использовать этот запрос создать себе класс, который я могу использовать?

+0

Что такое 'модель Social' ссылается FK из' Choice'? – van

+0

Извините, это должен быть Студент, обновленный. – Danosaure

ответ

3

Если вы можете создать это представление в базе данных, тогда вы просто сопоставляете представление, как если бы это была таблица. См. 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 _ 

Если вы не можете создать VIEW на уровне базы данных, можно создать выбираемый и карту ViewObject к нему.Приведенный ниже код должен дать вам идею:

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

Вы можете создать 3 запросов, как это, а затем объединить их с union_all и использовать полученный запрос в картографа:

view_mapper = mapper(ViewObject, my_combined_qry) 

В обоих случаях у вас есть чтобы убедиться, что PrimaryKey правильно определен в представлении, и вам может понадобиться override автозагрузочное представление и явно указать первичный ключ (см. ссылку выше). В противном случае вы либо получите сообщение об ошибке, либо не получите правильные результаты запроса.


Ответ на РЕДАКТИРОВАТЬ-2:

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

В результате будет кортеж пар: (Student, Choice).
Но если вы хотите создать новый отображенный класс для запроса, то вы можете создать выбираемый как образец выше:

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

Спасибо, но у меня нет доступа к модификации в БД, а только для доступа к этой базе данных только для чтения. Хотя я создаю представление в своей базе данных, но это может не работать, если базы данных находятся на разных службах. – Danosaure

+0

@Danosaure: отредактировал ответ с возможностью сопоставить его, когда не удается создать объекты БД. – van

+0

Можно ли это сделать декларативной базой? Я установил свои классы. У меня есть проблема для определения 'condition == 'A'' в моем' primaryjoin' 'orm.relationship'. – Danosaure

1

Это, чтобы показать, что я в конечном итоге делает, никаких комментариев приветствоваться.


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

и я остановлюсь оттуда ...

Благодаря @van

Смежные вопросы