SQLAlchemy 根据另一个表中的值过滤表的最佳方法

Posted

技术标签:

【中文标题】SQLAlchemy 根据另一个表中的值过滤表的最佳方法【英文标题】:SQLAlchemy best way to filter a table based on values from another table 【发布时间】:2021-03-23 15:49:49 【问题描述】:

如果我的问题是平庸的,我提前道歉:我是 SQL 的初学者。

我想创建一个简单的数据库,其中包含两个表:StudentsAnswers。 基本上,每个学生将回答三个问题(每个问题的可能答案是TrueFalse),他的答案将存储在Answers 表中。 Students 可以有两个“经验”级别:“本科”和“研究生”。 获得Students 提供的具有“研究生”经验级别的所有Answers 的最佳方法是什么?

这就是我为 StudentsAnswers 表中的条目定义 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.idAnswer.answer。无需使用with 语句或打印行。

以上是关于SQLAlchemy 根据另一个表中的值过滤表的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

根据另一个表的值选择表和字段

如何根据另一个表中的值创建重复记录

怎么根据一个表的字段值修改另一个表的字段值

如何根据与另一个表中的值的比较来更新列

MySQL:如何根据从另一个表中选择的值填充现有表的新列[关闭]

如何根据列的值过滤 SQLAlchemy 结果?