代码之家  ›  专栏  ›  技术社区  ›  Muhammad Hassan

如何在Sqlalchemy中实现右/左/内连接

  •  0
  • Muhammad Hassan  · 技术社区  · 5 年前

    我有两张桌子 beard moustache 定义如下:

    +--------+---------+------------+-------------+
    | person | beardID | beardStyle | beardLength |
    +--------+---------+------------+-------------+
    
    +--------+-------------+----------------+
    | person | moustacheID | moustacheStyle |
    +--------+-------------+----------------+
    

    我在PostgreSQL中创建了一个SQL查询,它将组合这两个表并生成以下结果:

    +--------+---------+------------+-------------+-------------+----------------+
    | person | beardID | beardStyle | beardLength | moustacheID | moustacheStyle |
    +--------+---------+------------+-------------+-------------+----------------+
    | bob    | 1       | rasputin   | 1           |             |                |
    +--------+---------+------------+-------------+-------------+----------------+
    | bob    | 2       | samson     | 12          |             |                |
    +--------+---------+------------+-------------+-------------+----------------+
    | bob    |         |            |             | 1           | fu manchu      |
    +--------+---------+------------+-------------+-------------+----------------+
    

    查询:

    SELECT * FROM beards LEFT OUTER JOIN mustaches ON (false) WHERE  person = "bob"
    UNION ALL
    SELECT * FROM beards b RIGHT OUTER JOIN mustaches ON (false) WHERE  person = "bob"
    

    然而,我无法创建它的SQLAlchemy表示。我尝试了几种方法来实现 from_statement outerjoin 但它们都没有真正起作用。有人能帮我吗?

    0 回复  |  直到 13 年前
        1
  •  10
  •   Tim    6 年前

    在SQL中, A RIGHT OUTER JOIN B 相当于 B LEFT OUTER JOIN A .所以,从技术上讲,没有必要 RIGHT OUTER JOIN API-可以通过切换目标“可选”和连接“可选”的位置来执行相同的操作。SQL Alchemy为此提供了一个API:

    # this **fictional** API:
    query(A).join(B, right_outer_join=True)  # right_outer_join doesn't exist in SQLA!
    
    # can be implemented in SQLA like this:
    query(A).select_entity_from(B).join(A, isouter=True)
    

    参见SQLA Query.join() doc 部分 “控制从何处加入” .

        2
  •  5
  •   Community Mohan Dere    8 年前

    从…起 @Francis P 是的 suggestion 我想到了这个片段:

    q1 = session.\
         query(beard.person.label('person'),
               beard.beardID.label('beardID'),
               beard.beardStyle.label('beardStyle'),
               sqlalchemy.sql.null().label('moustachID'),
               sqlalchemy.sql.null().label('moustachStyle'),
         ).\
         filter(beard.person == 'bob')
    
    q2 = session.\
         query(moustache.person.label('person'),
               sqlalchemy.sql.null().label('beardID'), 
               sqlalchemy.sql.null().label('beardStyle'),
               moustache.moustachID,
               moustache.moustachStyle,
         ).\
         filter(moustache.person == 'bob')
    
    result = q1.union(q2).all()
    

    然而,这是可行的,但你不能称之为一个答案,因为它似乎是一个黑客。这又是一个原因,为什么应该有 RIGHT OUTER JOIN 在炼金术中。

        3
  •  2
  •   yabin    5 年前

    如果A、B是表格,则可以实现:
    SELECT * FROM A RIGHT JOIN B ON A.id = B.a_id WHERE B.id = my_id
    作者:
    SELECT A.* FROM B JOIN ON A.id = B.a_id WHERE B.id = my_id
    在炼金术中:

    from sqlalchemy import select
    
    
    result = session.query(A).select_entity_from(select([B]))\
        .join(A, A.id == B.a_id)\
        .filter(B.id == my_id).first()
    

    例如:

    # import ...
    
    class User(Base):
        __tablenane = "user"
    
        id = Column(Integer, primary_key=True)
        group_id = Column(Integer, ForeignKey("group.id"))
    
    class Group(Base):
        __tablename = "group"
    
        id = Column(Integer, primary_key=True)
        name = Column(String(100))
    

    您可以通过以下代码按用户id获取用户组名称:

    # import ...
    from sqlalchemy import select
    
    user_group_name, = session.query(Group.name)\
        .select_entity_from(select([User]))\
        .join(Group, User.group_id == Group.id)\
        .filter(User.id == 1).first()
    

    如果需要外部联接,请使用 outerjoin() 而不是 join() .

    这个答案是对前一个答案(帖木儿的答案)的补充。

        4
  •  1
  •   Eric    11 年前

    以下是我的资料,ORM style:

    from sqlalchemy.sql import select, false
    
    stmt = (
        select([Beard, Moustache])
        .select_from(
            outerjoin(Beard, Moustache, false())
        ).apply_labels()
    ).union_all(
        select([Beard, Moustache])
        .select_from(
            outerjoin(Moustache, Beard, false())
        ).apply_labels()
    )
    
    session.query(Beard, Moustache).select_entity_from(stmt)
    

    这似乎是它自己的工作,但似乎不可能加入另一个选择表达

        5
  •  0
  •   rayjc    5 年前

    不幸的是,SQLAlchemy只为 LEFT OUTER JOIN .outerjoin() .如前所述 above ,我们可以找一个 RIGHT OUTER JOIN 通过反转 左外接 ; 如。 A RIGHT JOIN B B LEFT JOIN A . 在SQL中,以下语句是等效的:

    SELECT * FROM A RIGHT OUTER JOIN B ON A.common = B.common;
    SELECT * FROM B LEFT OUTER JOIN A ON A.common = B.common;
    

    然而,在SQLAlchemy中,我们需要查询一个类,然后执行 join .棘手的部分是重写SQLAlchemy语句以反转表格。例如,下面前两个查询的结果是不同的,因为它们返回不同的对象。

    # No such API (rightouterjoin()) but this is what we want.
    # This should return the result of A RIGHT JOIN B in a list of object A
    session.query(A).rightouterjoin(B).all()   # SELECT A.* FROM A RIGHT OUTER JOIN B ...
    
    # We could reverse A and B but this returns a list of object B
    session.query(B).outerjoin(A).all()        # SELECT B.* FROM B LEFT OUTER JOIN A ...
    
    # This returns a list of object A by choosing the 'left' side to be B using select_from()
    session.query(A).select_from(B).outerjoin(A).all()   # SELECT A.* FROM B LEFT OUTER JOIN A ...
    
    # For OP's example, assuming we want to return a list of beard object:
    session.query(beard).select_from(moustache).outerjoin(beard).all()
    

    再加上答案,你可以找到 select_from SQLAlchemy doc .