Как я могу лучше оптимизировать ассоциативные запросы «многие ко многим» с помощью DataMapper?

#ruby #datamapper

#ruby #datamapper

Вопрос:

DataMapper, похоже, генерирует крайне неоптимальные запросы для ассоциаций, которые используют таблицу объединения. Что я могу сделать, чтобы повысить производительность этих запросов? Обратите внимание, что он также генерирует те же запросы с неявной таблицей объединения ( :through => Resource )

Вот моя настройка:

 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
  

Теперь, допустим, у меня есть некоторые заполненные данные, и я хочу получить все права, связанные с левым объектом:

 Left.first.rights
  

DataMapper выполняет следующий запрос:

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 генерирует совершенно ненужный JOIN (первый выделенный жирным шрифтом раздел). Я не доверяю своей СУБД (MySQL), чтобы она умела игнорировать это. В плане объяснения это отображается как «Использование индекса; Использование временного; Использование сортировки файлов», по крайней мере.

  • Что случилось с GROUP BY ? Здесь это также кажется совершенно ненужным — из-за составного ключа в таблице объединения у меня не может быть дубликатов. Кроме того, не будет GROUP BY rights.id такого же эффекта? Или даже лучше, SELECT DISTINCT

Эти запросы выполняются чрезвычайно медленно (для таблиц с большим количеством свойств с обеих сторон), и я не уверен, как правильно индексировать таблицы для его поддержки.

Гораздо быстрее выполнять запросы из модели объединения: Join.all(:left => Left.first).rights , хотя она выполняет два оператора:

 SELECT right_id FROM joins WHERE left_id = 1
SELECT id, one, two FROM rights WHERE id = 1 ORDER BY id
  

Интересно, что Left.first.joins.rights идет тем же маршрутом и выполняет два приведенных выше запроса.

Комментарии:

1. Ваш вопрос действительно «почему?» или, возможно, «как мне избежать этого»?

Ответ №1:

Как насчет удаления определения класса Join и определения Left как:

 class Left
  include DataMapper::Resource

  property :id, Serial

  has n, :rights, :through => Resource
end
  

Поверьте DM, это создает хорошее сопоставление для вас.