ใช้หรือใน SQLAlchemy


191

ฉันได้ดูเอกสารแล้วและฉันไม่สามารถหาวิธีที่จะทำแบบสอบถาม OR ใน SQLAlchemy ได้ ฉันแค่ต้องการทำแบบสอบถามนี้

SELECT address FROM addressbook WHERE city='boston' AND (lastname='bulger' OR firstname='whitey')

ควรเป็นสิ่งที่ชอบ

addr = session.query(AddressBook).filter(City == "boston").filter(????)

คำตอบ:


322

จากบทช่วยสอน :

from sqlalchemy import or_
filter(or_(User.name == 'ed', User.name == 'wendy'))

72
โปรดทราบว่าวิธีการนี้สนับสนุนการใช้เครื่องกำเนิดไฟฟ้าดังนั้นหากคุณมีรายการสิ่งต่าง ๆ ที่ยาวถึง OR คุณสามารถทำได้filter(or_(User.name == v for v in ('Alice', 'Bob', 'Carl')))
robru

66
คำแนะนำของ @Rruru ไม่มีประสิทธิภาพโดยไม่จำเป็น หากคุณมีคอลเล็กชันอยู่แล้วคุณควรใช้in_โอเปอเรเตอร์ดังนี้:filter(User.name.in_(['Alice', 'Bob', 'Carl']))
intgr

5
อ่าขอบคุณฉันไม่ทราบว่า sqlalchemy มีตัวกรองนั้น
robru

8
@intgr ตัวอย่างที่แสดงโดย robru ยังคงมีประสิทธิภาพถ้าคุณต้องการใช้ตัวดำเนินการอื่นแทน in_ ตัวอย่างเช่นตัวดำเนินการ LIKE
Lhassan Baazzi

2
@intgr ประสบการณ์ของฉันกับ Oracle แสดงว่าลำดับ "OR" เร็วกว่าการใช้ "IN" นอกจากนี้ "IN" ยัง จำกัด อยู่ที่ชุดของ ~ 1,000 รายการในขณะที่ "หรือ" ไม่
ga

321

SQLAlchemy overloads ประกอบบิต&, |และ~ดังนั้นแทนที่จะน่าเกลียดและยากต่อการอ่านคำนำหน้าไวยากรณ์ด้วยor_()และand_()(เหมือนในคำตอบของ Bastien ) คุณสามารถใช้ประกอบการเหล่านี้:

.filter((AddressBook.lastname == 'bulger') | (AddressBook.firstname == 'whitey'))

โปรดทราบว่าวงเล็บไม่ใช่ทางเลือกเนื่องจากความสำคัญของตัวดำเนินการ bitwise

ดังนั้นข้อความค้นหาทั้งหมดของคุณอาจมีลักษณะเช่นนี้:

addr = session.query(AddressBook) \
    .filter(AddressBook.city == "boston") \
    .filter((AddressBook.lastname == 'bulger') | (AddressBook.firstname == 'whitey'))

8
+1 แต่คุณสามารถรวมอาร์กิวเมนต์ตัวกรองสองตัวสุดท้ายไว้ในวงเล็บมากขึ้นและใช้&ระหว่างพวกเขากับตัวแรก (แทนการใช้การfilterเรียกครั้งที่สอง) แทนเอฟเฟกต์เดียวกันได้หรือไม่
Chase Sandmann

21
@ChaseSandmann: ใช่แล้ว แต่มันจะอ่านง่ายขึ้นหรือไม่ ไม่
ThiefMaster

1
คงจะดีถ้ามีลิงค์ไปยัง SQLAlchemy docs ที่นี่!
Cheche

@ThiefMaster บังเอิญที่ชื่อแทนของคุณมีขโมยและคุณมี Whitey Bulger ในตัวอย่างของคุณหรือไม่
TheRealChx101

36

or_() ฟังก์ชั่นจะมีประโยชน์ในกรณีที่ไม่ทราบจำนวนขององค์ประกอบแบบสอบถามหรือ

ตัวอย่างเช่นสมมติว่าเรากำลังสร้างบริการ REST พร้อมตัวกรองเสริมบางตัวที่ควรส่งคืนเร็กคอร์ดหากตัวกรองใด ๆ คืนค่าเป็นจริง อีกด้านหนึ่งหากพารามิเตอร์ไม่ได้ถูกกำหนดในคำขอแบบสอบถามของเราไม่ควรเปลี่ยนแปลง หากไม่มีor_()ฟังก์ชั่นเราจะต้องทำสิ่งนี้:

query = Book.query
if filter.title and filter.author:
    query = query.filter((Book.title.ilike(filter.title))|(Book.author.ilike(filter.author)))
else if filter.title:
    query = query.filter(Book.title.ilike(filter.title))
else if filter.author:
    query = query.filter(Book.author.ilike(filter.author))

ด้วยor_()ฟังก์ชั่นสามารถเขียนใหม่เป็น:

query = Book.query
not_null_filters = []
if filter.title:
    not_null_filters.append(Book.title.ilike(filter.title))
if filter.author:
    not_null_filters.append(Book.author.ilike(filter.author))

if len(not_null_filters) > 0:
    query = query.filter(or_(*not_null_filters))

1
คำตอบที่เป็นประโยชน์มาก
Ray Toal

3

สิ่งนี้มีประโยชน์จริงๆ นี่คือการดำเนินการของฉันสำหรับตารางที่กำหนด:

def sql_replace(self, tableobject, dictargs):

    #missing check of table object is valid
    primarykeys = [key.name for key in inspect(tableobject).primary_key]

    filterargs = []
    for primkeys in primarykeys:
        if dictargs[primkeys] is not None:
            filterargs.append(getattr(db.RT_eqmtvsdata, primkeys) == dictargs[primkeys])
        else:
            return

    query = select([db.RT_eqmtvsdata]).where(and_(*filterargs))

    if self.r_ExecuteAndErrorChk2(query)[primarykeys[0]] is not None:
        # update
        filter = and_(*filterargs)
        query = tableobject.__table__.update().values(dictargs).where(filter)
        return self.w_ExecuteAndErrorChk2(query)

    else:
        query = tableobject.__table__.insert().values(dictargs)
        return self.w_ExecuteAndErrorChk2(query)

# example usage
inrow = {'eqmtvs_id': eqmtvsid, 'datetime': dtime, 'param_id': paramid}

self.sql_replace(tableobject=db.RT_eqmtvsdata, dictargs=inrow)

ขออภัยฉันทำผิดพลาดเล็กน้อยบรรทัดต่อไปนี้: query = select ([tableobject]) โดยที่ (และ _ (* filterargs))
delpozov
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.