Мои Rails-запросы начинают усложняться, я должен перейти на необработанные SQL-запросы? Чем ты занимаешься? - PullRequest
7 голосов
/ 17 июня 2009

Моему приложению Rails начинают требоваться сложные запросы. Должен ли я просто начать использовать сырые запросы SQL? Какова тенденция в сообществе Rails?

Обновление:

У меня сейчас нет письменных запросов, я хотел задать этот вопрос перед тем, как начать. Но вот пример того, что я хочу сделать:

У меня есть книги, в которых есть категории. Я хочу сказать -

Give me all books that were: 
-created_at (added to store) between date1 and date2
-updated_at before date3
-joined with books that exist in shopping carts right now

Я еще не написал запрос, но я думаю, что версия rails будет примерно такой:

books_to_consider = Book.find(:all, 
                       :conditions => "created_at <= '#{date2}' AND created_at >= '#{date1}' AND updated_at <= '#{date3}'",
                       :joins => "as b inner join carts as c on c.book_id = b.id")

Я не говорю, что ActiveRecord не может обработать этот запрос, но более ли приемлемо использовать сырой SQL для удобства чтения (или, может быть, есть другие ограничения, о которых я пока не знаю)?

Ответы [ 2 ]

13 голосов
/ 17 июня 2009

Общая идея состоит в том, чтобы придерживаться максимально генерируемых запросов ActiveRecord и использовать фрагменты SQL только при необходимости . Фрагменты SQL явно поддерживаются, потому что создатели ActiveRecord поняли, что SQL нельзя полностью абстрагировать.

Использование метода find без фрагментов SQL, как правило, вознаграждается за лучшую поддержку. Учитывая ваш пример, попробуйте:

Book.find(:all,
  :conditions => ["created_at >= ? AND created_at <= ? AND updated_at <= ?", 
                  date1, date2, date3]
  :include => :carts)

:inlude => :carts выполнит объединение, если вы добавили has_many :carts к вашей Book модели. Как вы можете видеть, нет необходимости в большом количестве SQL. Даже цитирование и экранирование ввода могут быть оставлены на Rails, при этом все еще используются литералы SQL для обработки операторов >= и <=.

Пройдя немного дальше, вы можете сделать это еще яснее:

class Book < AciveRecord::Base
  # Somewhere in your Book model:
  named_scope :created_between, lambda { |start_date, end_date|
    { :conditions => { :created_at => start_date..end_date } }
  }
  named_scope :updated_before, lambda { |date|
    { :conditions => ["updated_at <= ?", date] }
  }
  # ...
end

Book.created_between(date1, date2).updated_before(date3).find(:all,
  :include => :carts)

Обновление: точка named_scope s, конечно, повторно использовать условия. Вам решать, имеет ли смысл помещать набор условий в именованную область или нет.

3 голосов
/ 17 июня 2009

Как говорит Мольф с: include, .find () имеет преимущество в стремлении загружать детей. Кроме того, есть несколько плагинов, таких как нумерация страниц, которые обернут функцию поиска. Вы должны будете использовать .find (), чтобы использовать плагины.

Если у вас действительно сложный SQL-запрос, помните, что .find () использует вашу строку параметров. Вы всегда можете ввести свой собственный SQL-код:

: условия => ["идентификатор в объединении (выберите * из таблицы ...

И не забывайте, что для .find ()

существует множество необязательных параметров.
  • : условия - фрагмент SQL, такой как "administrator = 1", ["user_name =?", Username] или ["user_name =: user_name", {: user_name => user_name}]. Смотрите условия во вступлении.
  • : order - фрагмент SQL, такой как «made_at DESC, name».
  • : group - имя атрибута, по которому должен быть сгруппирован результат. Использует SQL-предложение GROUP BY.
  • : has - В сочетании с +: group + это можно использовать для фильтрации записей, возвращаемых GROUP BY. Использует SQL-предложение HAVING.
  • : limit - Целое число, определяющее ограничение на число строк, которые должны быть возвращены.
  • : смещение - целое число, определяющее смещение, из которого должны быть извлечены строки. Таким образом, в 5, это будет пропускать строки с 0 по 4.
  • : объединения - Либо фрагмент SQL для дополнительных объединений, таких как «LEFT JOIN comments ON comments.post_id = id» (редко требуется), именованные ассоциации в той же форме, что и для опции: include, которая будет выполнять INNER JOIN связанные таблицы или массив, содержащий смесь как строк, так и именованных ассоциаций. Если значение является строкой, то записи будут возвращены только для чтения, поскольку они будут иметь атрибуты, которые не соответствуют столбцам таблицы. Pass: readonly => false для переопределения.
  • : include - имена ассоциаций, которые должны быть загружены вместе. Названные символы относятся к уже определенным ассоциациям. См. Нетерпеливую загрузку под Ассоциациями.
  • : select - По умолчанию это «*», как в «SELECT * FROM», но его можно изменить, если, например, вы хотите выполнить объединение, но не включать объединенные столбцы. Принимает строку с фрагментом SELECT SQL (например, «id, name»).
  • : from - по умолчанию это имя таблицы класса, но его можно изменить на альтернативное имя таблицы (или даже имя представления базы данных).
  • : только чтение - отметьте возвращенные записи только для чтения, чтобы их нельзя было сохранить или обновить.
  • : блокировка - фрагмент SQL, например "FOR UPDATE" или "LOCK IN SHARE MODE". : lock => true дает эксклюзивную блокировку подключения по умолчанию, обычно "FOR UPDATE".

Источник: http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M002553

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...