Как я могу увидеть SQL, который будет сгенерирован заданным запросом ActiveRecord в Ruby on Rails


116

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

Например:

SampleModel.find(:all, :select => "DISTINCT(*)", :conditions => ["`date` > #{self.date}"], :limit => 1, :order => '`date`', :group => "`date`")

Я хотел бы открыть консоль irb и добавить в конец метод, который бы показывал SQL, который будет генерировать этот запрос, но не обязательно выполнять запрос.

Ответы:


12

Когда я последний раз пытался сделать это, официального способа сделать это не было. Я прибег к использованию функции, которую findи ее друзья используют для непосредственной генерации запросов. Это частный API, поэтому существует огромный риск того, что Rails 3 полностью его сломает, но для отладки это подходящее решение.

Вам придется использовать метод construct_finder_sql(options)( lib/active_record/base.rb:1681), sendпотому что он частный.

Изменить : construct_finder_sql был удален в Rails 5.1.0.beta1 .


1
Это близко к тому, о чем я думаю. Думаю, можно написать плагин, который будет делать что-то вроде: SampleModel.find (: all,: select => "DISTINCT (*)" date,: conditions => [" > # {self.date}"],: limit => 1 ,: order => ' date',: group => " date") .show_generated_sql и пусть это вызовет метод construct_finder_sql.
rswolff

1
С DataMapper вы могли бы это сделать, потому что он не запускает запрос сразу. ActiveRecord, с другой стороны, выполняет запрос немедленно. show_generated_sqlбудет работать с уже полученным набором данных из find.
Джон Ф. Миллер,

4
In Rails 3 construct_finder_sqlдействительно удален
Veger

190

Подобно Penger's, но работает в любое время в консоли, даже после того, как классы были загружены и регистратор был кэширован:

Для Rails 2:

ActiveRecord::Base.connection.instance_variable_set :@logger, Logger.new(STDOUT)

Для Rails 3.0.x:

ActiveRecord::Base.logger = Logger.new(STDOUT)

Для Rails> = 3.1.0 это уже сделано по умолчанию в консолях. Если он слишком шумный и вы хотите его выключить, вы можете:

ActiveRecord::Base.logger = nil

У меня это не работает rails console. Работает только на irb, загруженном прямо из оболочки? (или он был удален для рельсов 3?)
Эрик Ху

2
Перенесли его в более разумное место для Rails 3 ... см. Мою обновленную версию.
gtd 07

Есть ли способ делать это автоматически каждый раз при запуске консоли? Что-то вроде крючка перед загрузкой?
stream7 06

1
@ stream7..Я не знаю, нужно ли вам это сейчас, но вы можете переместить этот код в environment.rb.. if "irb" == $0;ActiveRecord::Base.logger = Logger.new(STDOUT);end..получил это из комментариев в http://weblog.jamisbuck.org/2007/1/8/watching-activerecord-do -it-s-thing
rubyprince

Это не отвечает на вопрос: как показать sql для определенного запроса во время отладки без выполнения запроса.
bradw2k

87

Прилепите puts query_object.class куда-нибудь, чтобы увидеть, с каким типом объекта вы работаете, а затем посмотрите документацию.

Например, в Rails 3.0 области видимости используют ActiveRecord::Relationwhich has #to_sqlmethod. Например:

class Contact < ActiveRecord::Base
  scope :frequently_contacted, where('messages_count > 10000')
end

Тогда где-нибудь вы можете сделать:

puts Contact.frequently_contacted.to_sql

3
Почему этот ответ не получил поддержки? Для Rails 3 это лучший ответ - вы можете получить результаты любого оператора AR :: Relation, просто поместив ".to_sql" в конец. Этот вопрос также дает ответ: stackoverflow.com/questions/3814738/…
Стив Мидгли

5
Осторожно: вы не получите все SQL, если у вас есть includesродственник,
Барри Келли,

3
@BarryKelly Почему это так?
Вишну Наранг

25

Это может быть старый вопрос, но я использую:

SampleModel.find(:all,
                 :select => "DISTINCT(*)",
                 :conditions => ["`date` > #{self.date}"], 
                 :limit=> 1, 
                 :order => '`date`',
                 :group => "`date`"
                 ).explain

explainМетод даст довольно подробное заявление SQL о том , что его собираются сделать


3
Он также выполнит запрос, который может быть не тем, что нужно людям, только для записи.
Ибрагим

22

просто используйте to_sqlметод, и он выведет запрос sql, который будет запущен. он работает с активной записью.

irb(main):033:0> User.limit(10).where(:username => 'banana').to_sql
=> "SELECT  "users".* FROM "users"  WHERE "users"."username" = 'banana'
LIMIT 10"

при этом findон не сработает, поэтому вам нужно будет добавить этот идентификатор вручную в запрос или запустить его, используя where.

irb(main):037:0* User.where(id: 1).to_sql
=> "SELECT "users".* FROM "users"  WHERE "users"."id" = 1"

11

Это то, что я обычно делаю, чтобы сгенерировать SQL в консоли.

-> script/console
Loading development environment (Rails 2.1.2)
>> ActiveRecord::Base.logger = Logger.new STDOUT
>> Event.first

Вы должны сделать это при первом запуске консоли, если вы сделаете это после того, как наберете код, похоже, это не сработает.

Не могу поверить в это, нашел это давным-давно в чьем-то блоге и не могу вспомнить, чей это.


1
Я почти уверен, что это был блог Джеймиса
rswolff

1
Я не уверен, связано ли это с Rails 2.3 или чем-то еще в моей среде, но у меня это не работает. Смотрите мой ответ ниже.
gtd

Это отличное решение IMO.
Бенджамин Аткин

10

Создайте файл .irbrc в своем домашнем каталоге и вставьте его в:

if ENV.include?('RAILS_ENV') && !Object.const_defined?('RAILS_DEFAULT_LOGGER')
  require 'logger'
  RAILS_DEFAULT_LOGGER = Logger.new(STDOUT)
end

Это будет выводить операторы SQL в ваш сеанс irb по мере вашего продвижения.

РЕДАКТИРОВАТЬ: Извините, что все еще будет выполнять запрос, но он самый близкий, о котором я знаю.

РЕДАКТИРОВАТЬ: Теперь с arel вы можете создавать области / методы, пока объект возвращает ActiveRecord :: Relation и вызывает на нем .to_sql, и он помещает sql, который будет выполняться.


Это то, что я делал, но мне больше интересно просто увидеть прогнозируемый SQL, который будет генерировать запрос ActiveRecord. Я удивлен, что нет простого способа сделать это ...
rswolff

5

Мой типичный способ узнать, какой sql он использует, - это ввести «ошибку» в sql, после чего вы получите сообщение об ошибке, выплевываемое на обычный регистратор (и веб-экран), на котором есть рассматриваемый sql. Не нужно искать, куда идет stdout ...


1
Эффектное, но самое быстрое решение :)
Ян Яночко

2

Попробуйте плагин show_sql . Плагин позволяет распечатать SQL без его запуска.

SampleModel.sql(:select => "DISTINCT(*)", :conditions => ["`date` > #{self.date}"], :limit => 1, :order => '`date`', :group => "`date`")

1
Похоже, ссылка не работает (ошибка 404). Вероятно, Райан Бигг удалил плагин.
DNNX

1

Вы можете изменить метод журнала подключения, чтобы вызвать исключение, предотвращающее выполнение запроса.

Это полный взлом, но мне кажется, что он работает (Rails 2.2.2, MySQL):

module ActiveRecord
  module ConnectionAdapters
    class AbstractAdapter
      def log_with_raise(sql, name, &block)
        puts sql
        raise 'aborting select' if caller.any? { |l| l =~ /`select'/ }
        log_without_raise(sql, name, &block)
      end
      alias_method_chain :log, :raise
    end
  end
end

0

В Rails 3 вы можете добавить эту строку в config / environment / development.rb

config.active_record.logger = Logger.new(STDOUT)

Однако он выполнит запрос. Но половина ответила:

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.