SQLAlchemy 根据另一个表中的值过滤表的最佳方法
Posted
技术标签:
【中文标题】SQLAlchemy 根据另一个表中的值过滤表的最佳方法【英文标题】:SQLAlchemy best way to filter a table based on values from another table 【发布时间】:2021-03-23 15:49:49 【问题描述】:如果我的问题是平庸的,我提前道歉:我是 SQL 的初学者。
我想创建一个简单的数据库,其中包含两个表:Students
和 Answers
。
基本上,每个学生将回答三个问题(每个问题的可能答案是True
或False
),他的答案将存储在Answers
表中。
Students
可以有两个“经验”级别:“本科”和“研究生”。
获得Students
提供的具有“研究生”经验级别的所有Answers
的最佳方法是什么?
这就是我为 Students
和 Answers
表中的条目定义 SQLAlchemy 类的方式:
import random
from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, String, Date, Boolean, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
db_uri = "sqlite:///simple_answers.db"
db_engine = create_engine(db_uri)
db_connect = db_engine.connect()
Session = sessionmaker()
Session.configure(bind=db_engine)
db_session = Session()
Base = declarative_base()
class Student(Base):
__tablename__ = "Students"
id = Column(Integer, primary_key=True)
experience = Column(String, nullable=False)
class Answer(Base):
__tablename__ = "Answers"
id = Column(Integer, primary_key=True)
student_id = Column(Integer, ForeignKey("Students.id"), nullable=False)
answer = Column(Boolean, nullable=False)
Base.metadata.create_all(db_connect)
然后,我在数据库中插入一些随机条目:
categories_experience = ["Undergraduate", "Graduate"]
categories_answer = [True, False]
n_students = 20
n_answers_by_each_student = 3
random.seed(1)
for _ in range(n_students):
student = Student(experience=random.choice(categories_experience))
db_session.add(student)
db_session.commit()
answers = [Answer(student_id=student.id, answer=random.choice(categories_answer))
for _ in range(n_answers_by_each_student)]
db_session.add_all(answers)
db_session.commit()
然后,我得到所有“研究生”学生中的Student.id
:
ids_graduates = db_session.query(Student.id).filter(Student.experience == "Graduate").all()
ids_graduates = [result.id for result in ids_graduates]
最后,我使用.in_
运算符从“毕业生”Students
中选择Answers
:
answers_graduates = db_session.query(Answer).filter(Answer.student_id.in_(ids_graduates)).all()
我手动检查了答案,它们是正确的。但是,由于我是 SQL 的初学者,我怀疑有更好的方法可以达到相同的结果。
有没有这样一种客观上“最好”的方式(更 Pythonic,更高效......)?我想用 SQLAlchemy 实现我的结果,可能使用 ORM 接口。
【问题讨论】:
【参考方案1】:当我问这个问题时,我很着急。 从那时起,我就有时间学习SQLAlchemy ORM documentation。 有两种推荐的方法可以根据另一个表中的值过滤表。
第一种方式其实和我最初尝试的很相似:
query_graduates = db_session.query(User.id).filter(User.experience == "Graduate")
query_answers_graduates = (db_session
.query(Answer)
.filter(Answer.user_id.in_(query_graduates))
)
answers_graduates = query_answers_graduates.all()
它使用.in_
运算符,它接受对象列表或其他查询作为参数。
第二种方式使用.join
方法:
query_answers_graduates = (db_session
.query(Answer)
.join(User)
.filter(User.experience == "Graduate")
)
第二种方法更简洁。我对这两种解决方案都进行了计时,第二种方法使用.join
,速度稍快。
【讨论】:
【参考方案2】:您提到了 SQL,但如果您想在 Python 或 SQL 中执行此特定步骤,我会感到困惑。如果是 SQL,这样的东西可以工作:
select * from Students s
inner join Answers a on s.id = a.student_id
where s.experience = "Graduate";
更新代码
我以前从未使用过 SQLAlchemy,但类似的东西可能会起作用...
sql = """select s.Id, a.answer from Students s
inner join Answers a on s.id = a.student_id
where s.experience = "Graduate";"""
with db_session as con:
rows = con.execute(sql)
for row in rows:
print(row)
【讨论】:
谢谢!是的,我想在 SQLAlchemy 中这样做,所以在 Python 中。您能否解释一下如何在 SQLAlchemy 中执行此操作,可能使用它的 ORM 接口? 我刚刚更新了代码。类似的东西可能会奏效。你可能需要稍微调整一下。也更新了,所以你正在拉特定列而不是 Select * 是的,它有效!我尝试过这个。解决方案是answers_graduates = db_engine.execute(sql).fetchall()
,如果您想为其他用户更新答案。 answers_graduates
是一个元组列表,每个元组包含“研究生”学生的Student.id
和Answer.answer
。无需使用with
语句或打印行。以上是关于SQLAlchemy 根据另一个表中的值过滤表的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章