[英]sqlalchemy: union query few columns from multiple tables with condition
I'm trying to adapt some part of a MySQLdb application to sqlalchemy in declarative base. 我正在尝试将MySQLdb应用程序的某些部分调整为声明式基础中的sqlalchemy。 I'm only beginning with sqlalchemy.
我只是从sqlalchemy开始。
The legacy tables are defined something like: 遗留表的定义如下:
student: id_number*, semester*, stateid, condition, ...
choice: id_number*, semester*, choice_id, school, program, ...
We have 3 tables for each of them ( student_tmp
, student_year
, student_summer
, choice_tmp
, choice_year
, choice_summer
), so each pair ( _tmp
, _year
, _summer
) contains information for a specific moment. 我们每个都有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`)
My problem is the information that is important to me is to get the equivalent of the following 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
*
used for shorten the select, but I'm actually only querying for about 7 columns out of the 50 availables. *
用于缩短选择,但我实际上只查询50个可用的大约7列。
This information is used in many flavors... "Do I have new students? Do I still have all students from a given date? Which students are subscribed after the given date? etc..." The result of this select statement is to be saved in another database. 这些信息用于多种口味......“我有新学生吗?我是否仍然拥有特定日期的所有学生?在给定日期之后订阅哪些学生?等等...”此选择声明的结果是保存在另一个数据库中。
Would it be possible for me to achieve this with a single view-like class? 我是否有可能通过一个类似视图的类来实现这一目标? The information is read-only so I don't need to be able to modify/create/delte.
信息是只读的,因此我不需要能够修改/创建/删除。 Or do I have to declare a class for each table (ending up with 6 classes) and every time I need to query, I have to remember to filter?
或者我必须为每个表声明一个类(最后有6个类),每次我需要查询时,我必须记得过滤?
Thanks for pointers. 谢谢你的指点。
EDIT : I don't have modification access to the database (I cannot create a view). 编辑 :我没有对数据库的修改访问权限(我无法创建视图)。 Both databases may not be on the same server (so I cannot create a view on my second DB).
两个数据库可能不在同一台服务器上(因此我无法在第二个数据库上创建视图)。
My concern is to avoid the full table scan before filtering on condition
and choice_id
. 我担心的是在过滤
condition
和choice_id
之前避免全表扫描。
EDIT 2 : I've set up declarative classes like this: 编辑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): ...
Now, the query that gives me correct SQL for one set of table is: 现在,为一组表提供正确SQL的查询是:
q = session.query(StudentYear, ChoiceYear) \
.select_from(StudentYear) \
.join(ChoiceYear) \
.filter(StudentYear.condition=='A') \
.filter(ChoiceYear.choice_id=='4')
but it throws an exception... 但它引发了一个例外......
"Could not locate column in row for column '%s'" % key)
sqlalchemy.exc.NoSuchColumnError: "Could not locate column in row for column '*'"
How do I use that query to create myself a class I can use? 如何使用该查询创建自己可以使用的类?
If you can create this view on the database, then you simply map the view as if it was a table. 如果可以在数据库上创建此视图,则只需将视图映射为表格。 See 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 _
If you cannot create a VIEW
on the database level, you could create a selectable and map the ViewObject
to it. 如果无法在数据库级别创建
VIEW
,则可以创建一个可选择的ViewObject
映射到它。 The code below should give you the idea: 下面的代码应该给你的想法:
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')
You can create 3 queries like this, then combine them with union_all and use the resulting query in the mapper: 您可以创建3个这样的查询,然后将它们与union_all结合使用,并在mapper中使用生成的查询:
view_mapper = mapper(ViewObject, my_combined_qry)
In both cases you have to ensure though that a PrimaryKey is properly defined on the view, and you might need to override
the autoloaded view, and specify the primary key explicitely (see the link above). 在这两种情况下,您必须确保在视图上正确定义了PrimaryKey,并且您可能需要
override
自动加载的视图,并明确指定主键(请参阅上面的链接)。 Otherwise you will either receive an error, or might not get proper results from the query. 否则,您将收到错误,或者可能无法从查询中获得正确的结果。
Answer to EDIT-2: 回答EDIT-2:
qry = (session.query(StudentYear, ChoiceYear).
select_from(StudentYear).
join(ChoiceYear).
filter(StudentYear.condition == 'A').
filter(ChoiceYear.choice_id == '4')
)
The result will be tuple pairs: (Student, Choice)
. 结果将是元组对:(
(Student, Choice)
。
But if you want to create a new mapped class for the query, then you can create a selectable as the sample above: 但是,如果要为查询创建新的映射类,则可以创建一个可选的上述示例:
student_tmp = StudentTmp.__table__
choice_tmp = ChoiceTmp.__table__
.... (see sample code above)
This is to show what I ended up doing, any comment welcomed. 这是为了表明我最终做了什么,欢迎任何评论。
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')
and I will elaborate from there... 我会从那里详细说明......
Thanks @van 谢谢@van
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.