sqlalchemy:union从具有条件的多个表中查询几列

时间:2012-05-23 20:37:39

标签: python mysql sqlalchemy

我正在尝试将MySQLdb应用程序的某些部分调整为声明式基础中的sqlalchemy。我只是从sqlalchemy开始。

遗留表的定义如下:

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

我们每个都有3个表格(student_tmpstudent_yearstudent_summerchoice_tmpchoice_yearchoice_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

*用于缩短选择范围,但我实际上只查询了50个可用内容中的大约7列。

这些信息用于多种口味......“我是否有新生?我是否仍然拥有特定日期的所有学生?在给定日期之后订阅哪些学生?等等...”此选择的结果语句将保存在另一个数据库中。

我是否有可能通过一个类似视图的课程实现这一目标?信息是只读的,因此我不需要能够修改/创建/删除。或者我必须为每个表声明一个类(最后有6个类),每次我需要查询时,我必须记得过滤?

感谢指点。

编辑:我没有对数据库的修改权限(我无法创建视图)。两个数据库可能不在同一台服务器上(因此我无法在第二个数据库上创建视图)。

我担心的是在过滤conditionchoice_id之前避免全表扫描。

编辑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 '*'"

如何使用该查询创建自己可以使用的类?

2 个答案:

答案 0 :(得分: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自动加载的视图,并明确指定主键(请参阅上面的链接)。否则,您将收到错误,或者可能无法从查询中获得正确的结果。


回答EDIT-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)

答案 1 :(得分: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