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 sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at https://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.