I've got a situation where SQLAlchemy (1.0.14, accessing MySQL via PyMySQL
0.7.6) seems to be issuing unnecessary SELECTs to an Association Object
table for instances that are already in the identity map.
Hopefully I've extracted the essentials of my setup in the pseudo-example
below. StatusBlock uses a composite foreign key constraint into AssocInfo,
which is the association object; MyThings are a main object that contain
many StatusBlock instances via the "statuses" relationship.
Things work fine functionally, but it seems that every time a StatusBlock
lazyloads and requires a specific AssocInfo instance, say with composite
key (100,1000), a SELECT is issued for it, even if that AssocInfo instance
is already in the identity map from having been loaded by another
StatusBlock.
You can see at the comment "#TROUBLE HERE" below, when SQLAlchemy issues a
SELECT to lazyload the StatusBlock from the second thing, it also issues a
SELECT to get the (100,1000) AssocInfo record, even though it is in the
identity map, having just been loaded for the first StatusBlock at the
"FIRST LOAD" comment. It is almost as if it thinks the (100,1000)
AssocInfo record it needs for the second StatusBlock instance is different
from the one it has from the first. The problem comes if I loop through a
couple thousand MyThing records, all indirectly accessing that same
AssocInfo record, there's a couple thousand unnecessary SELECTs issued,
which are probably contributing to a slowness I'm trying to speed up.
Is there something goofy about the way I've got this set up, or my
understanding of the identity map and SELECT issuing, that I could change
to eliminate the unnecessary SELECTs? I'm checking to see if they might be
some side effect of other things my full setup has (trying to model an
existing system), and I'll see if I can set up a test system with the exact
setup below to make sure the SELECTS are really generated in this
simplified case.
# A1 and A2 are the left/right classes/tables that AssocInfo associates to
class AssocInfo(MyAlchemyBase): # our Association Object
__tablename__ = "assoc"
key1 = Column(Integer, ForeignKey("A1.id"), primary_key=True)
key2 = Column(Integer, ForeignKey("A2.id"), primary_key=True)
id = Column(Integer)
# ... more fields
itsA1 = relationship(A1, backref="assocs")
itsA2 = relationship(A2)
class StatusBlock(MyAlchemyBase):
__table_args__ = (ForeignKeyConstraint(['k1', 'k2'], ['assoc.key1',
'assoc.key2']), {})
id = Column(Integer, primary_key=True)
thing_id = Column(Integer, ForeignKey('thing.id'))
# (k1,k2) is the composite key into AssocInfo, as per __table_args__
k1 = Column(Integer, ForeignKey("A1.id")
k2 = Column(Integer)
itsAssoc = relationship(AssocInfo)
class MyThings(MyAlchemyBase):
__tablename__ = "thing"
id = Column(Integer, primary_key=True)
statuses = relationship("StatusBlock", collection_class=
attribute_mapped_collection('k1'))
__mapper_args__ = {'polymorphic_identity': 0} # hopefully unrelated
polymorphic stuff
logging.basicConfig()
logging.getLogger('sqlalchemy.engine').setLevel(logging.DEBUG)
session = getASession()
thing1 = session.query(MyThings).get(1) # assume thing1 has a related
StatusBlock with k1=100 and k2=1000
print thing1.statuses[100].itsAssoc # FIRST LOAD
# lazyload will do a SELECT on "assoc" table to get (100,1000) instance
# confirm that the AssocInfo instance (100,1000) is in map
print session.identity_map.items() # yup
thing2 = session.query(MyThings).get(2) # different thing instance, but
also has a StatusBlock with k1=100 and k2=1000
print session.identity_map.items() # confirm that (AssocInfo, (100,1000))
is still in map
print thing2.statuses[100].itsAssoc # "TROUBLE HERE"
# this shouldn't have had to SELECT to "assoc" to get (100,1000) record,
but it did
"""just like it did as part of the first load, something like:
INFO:sqlalchemy.engine.base.Engine:SELECT AssocInfo.key1 AS
AssocInfo_key1...
FROM AssocInfo
WHERE AssocInfo.key2 = %(param_1)s AND AssocInfo.key1 = %(param_2)s
INFO:sqlalchemy.engine.base.Engine:{u'param_1': 1000, u'param_2': 100}
DEBUG:sqlalchemy.engine.base.Engine:Col (u'AssocInfo_key1',
u'AssocInfo_key2', ...)
DEBUG:sqlalchemy.engine.base.Engine:Row (100, 1000, ...)
"""
--
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.