使用唯一键返回多行?

时间:2016-06-23 09:06:20

标签: python sqlalchemy

鉴于这3个简单的SQLAlchemy模型:

In [17]: df.val.cumprod()
Out[17]:
a      1
b      2
c      6
d     24
e    120
Name: val, dtype: int64

我不明白这个问题的原因:

class Customer(ModelBase):
    __tablename__ = 'customers'

    id = sa.Column(sa.Integer, primary_key=True)
    uid = sa.Column(sa.Unicode, nullable=False, unique=True)


class Handset(ModelBase):
    __tablename__ = 'handsets'

    id = sa.Column(sa.Integer, primary_key=True)
    imei = sa.Column(sa.Unicode(15), nullable=False, unique=True)


class Channel(ModelBase):
    __tablename__ = 'channels'

    id = sa.Column(sa.Integer, primary_key=True)

    customer_id = sa.Column(sa.ForeignKey(Customer.id), nullable=False)
    handset_id = sa.Column(sa.ForeignKey(Handset.id), nullable=False)

    customer = relationship(Customer, backref='channels', lazy='joined')
    handset = relationship(Handset, backref='channels', lazy='joined')


    __table_args__ = (
        sa.Index('uk_channels_customer_handset',
                 customer_id, handset_id,
                 unique=True),
    )

如果有多个session.query(Channel).filter(Handset.imei == '1234', Customer.uid == 'test').one_or_none() multiple rows found相关联,但Channelhandset_id相关联,则此customer_id会执行c = aliased(Customer) h = aliased(Handset) session.query(Channel).enable_eagerloads(False).join(c).join(h).filter( c.uid == 'test', h.imei == '1234', ).one_or_none() 执行,但此查询可以正常运行:

{{1}}

如何通过急切加载客户来加载频道&手持机?

1 个答案:

答案 0 :(得分:4)

第一个查询在手机,客户和渠道之间产生隐式交叉连接(这是一个多行的来源)+ 2个左连接(来自客户和手机的急切加载),这可能不是您想要的:

In [7]: print(session.query(Channel).filter(Handset.imei == '1234', Customer.uid == 'test'))
SELECT channels.id AS channels_id, channels.customer_id AS channels_customer_id, channels.handset_id AS channels_handset_id, customers_1.id AS customers_1_id, customers_1.uid AS customers_1_uid, handsets_1.id AS handsets_1_id, handsets_1.imei AS handsets_1_imei 
FROM handsets, customers, channels LEFT OUTER JOIN customers AS customers_1 ON customers_1.id = channels.customer_id LEFT OUTER JOIN handsets AS handsets_1 ON handsets_1.id = channels.handset_id 
WHERE handsets.imei = ? AND customers.uid = ?

当您明确定义连接并禁用预先加载左连接时,第二个工作。第一个查询可以使用has()

In [17]: print(session.query(Channel).filter(Channel.handset.has(imei='1234'), 
                                             Channel.customer.has(uid='test')))
SELECT channels.id AS channels_id, channels.customer_id AS channels_customer_id, channels.handset_id AS channels_handset_id, customers_1.id AS customers_1_id, customers_1.uid AS customers_1_uid, handsets_1.id AS handsets_1_id, handsets_1.imei AS handsets_1_imei 
FROM channels LEFT OUTER JOIN customers AS customers_1 ON customers_1.id = channels.customer_id LEFT OUTER JOIN handsets AS handsets_1 ON handsets_1.id = channels.handset_id 
WHERE (EXISTS (SELECT 1 
FROM handsets 
WHERE handsets.id = channels.handset_id AND handsets.imei = ?)) AND (EXISTS (SELECT 1 
FROM customers 
WHERE customers.id = channels.customer_id AND customers.uid = ?))

从本质上讲,这将分别检查该通道是否存在具有给定谓词的手机和客户,然后将客户和手机连接到此通道。

您还可以通过an explicit join in the second query and to eager load using that指示SQLAlchemy使用contains_eager()

In [28]: print(session.query(Channel).\
    join(c).join(h).\
    options(contains_eager(Channel.handset, alias=h),
            contains_eager(Channel.customer, alias=c)).\
    filter(
        c.uid == 'test', h.imei == '1234',
    ))
SELECT customers_1.id AS customers_1_id, customers_1.uid AS customers_1_uid, handsets_1.id AS handsets_1_id, handsets_1.imei AS handsets_1_imei, channels.id AS channels_id, channels.customer_id AS channels_customer_id, channels.handset_id AS channels_handset_id 
FROM channels JOIN customers AS customers_1 ON customers_1.id = channels.customer_id JOIN handsets AS handsets_1 ON handsets_1.id = channels.handset_id 
WHERE customers_1.uid = ? AND handsets_1.imei = ?

在我看来是更清洁的解决方案。