How can I better optimize many-to-many association queries with DataMapper?
DataMapper appears to be generating grossly sub-optimal queries for associations that use a join table. What can I do to improve the performance of these queries? Note that it generates the same queries with an implicit joi开发者_StackOverflow中文版n table, as well (:through => Resource
)
Here's my setup:
class Left
include DataMapper::Resource
property :id, Serial
has n, :joins
has n, :rights, :through => :joins
end
class Join
include DataMapper::Resource
belongs_to :left, :key => true
belongs_to :right, :key => true
end
class Right
include DataMapper::Resource
property :id, Serial
property :one, String
property :two, Integer
end
Now, say I have some data populated and I want to grab all the rights associated with a left object:
Left.first.rights
DataMapper executes the following query:
SELECT rights.id, rights.one, rights.two FROM rights INNER JOIN joins ON rights.id = joins.right_id
INNER JOIN lefts ON joins.left_id = lefts.id
WHERE joins.left_id = 1
GROUP BY rights.id, rights.one, rights.two
ORDER BY rights.id
DataMapper is generating a completely unnecessary
JOIN
(first bold section). I don't trust my RDBMS (MySQL) to be smart about ignoring this. It shows up in the explain plan, as "Using index; Using temporary; Using filesort", at least.What's up with the
GROUP BY
? It also seems completely unnecessary here - due to the composite key on the join table, I can't have duplicates. Plus, wouldn'tGROUP BY rights.id
have the same effect? Or even better,SELECT DISTINCT
These queries are extremely slow (on tables with more properties on both sides), and I'm not sure how to properly index the tables to support it.
It's far faster to query from the join model: Join.all(:left => Left.first).rights
, though it is performing two statements:
SELECT right_id FROM joins WHERE left_id = 1
SELECT id, one, two FROM rights WHERE id = 1 ORDER BY id
Interestingly, Left.first.joins.rights
goes the same route and executes the two queries above.
How about removing Join class definition, and defining Left as:
class Left
include DataMapper::Resource
property :id, Serial
has n, :rights, :through => Resource
end
Believe DM, it creates good mapping for you.
精彩评论