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


116

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

Наприклад:

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 (: всі,: select => "DISTINCT (*)" date,: умови => [" > # {self.date}"],: limit => 1 ,: order => ' date',: group => " date") .show_generated_sql і має цей виклик метод construct_finder_sql.
rswolff

1
З DataMapper ви могли, тому що він не запускає запит відразу. З іншого боку, ActiveRecord виконує запит негайно. show_generated_sqlбуде діяти на вже отриманому наборі даних з find.
Джон Ф. Міллер

4
У Rails 3 construct_finder_sqlсправді знято
Вегер

190

Подібний до Penger, але працює в будь-який час на консолі навіть після завантаження класів і кешування журналу:

Для рейок 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

Чи є спосіб зробити це автоматично кожного разу при запуску консолі? Щось схоже на гачок перед завантаженням?
потік7

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::Relationяких має #to_sqlметод. Наприклад:

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

Тоді десь можна зробити:

puts Contact.frequently_contacted.to_sql

3
Чому ця відповідь не підтримується? Для Rails 3 це спосіб краще відповісти - ви можете отримати результати будь-якої заяви AR: відношення, просто поставивши ".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він не працюватиме, тому вам потрібно буде додати цей ідентифікатор вручну до запиту або запустити його, використовуючи де.

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

Це чудове рішення ІМО.
Бенджамін Аткін

10

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

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

Це виводить оператори SQL у ваш irb сеанс у процесі роботи.

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

EDIT: Тепер за допомогою 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.