Bagaimana saya bisa melihat SQL yang akan dihasilkan oleh permintaan ActiveRecord tertentu di Ruby on Rails

116

Saya ingin melihat pernyataan SQL yang akan dihasilkan oleh Query ActiveRecord tertentu. Saya menyadari bahwa saya bisa mendapatkan informasi ini dari log setelah kueri dikeluarkan, tetapi saya bertanya-tanya apakah ada metode yang dapat dipanggil dan Permintaan ActiveRecord.

Sebagai contoh:

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

Saya ingin membuka konsol irb dan memasang metode di bagian akhir yang akan menunjukkan SQL yang akan dihasilkan oleh kueri ini, tetapi tidak harus menjalankan kueri.

rswolff
sumber

Jawaban:

12

Terakhir kali saya mencoba melakukan ini, tidak ada cara resmi untuk melakukannya. Saya terpaksa menggunakan fungsi yang finddigunakan teman-temannya untuk menghasilkan kueri mereka secara langsung. Ini adalah API pribadi sehingga ada risiko besar bahwa Rails 3 benar-benar akan merusaknya, tetapi untuk debugging, ini adalah solusi yang baik.

Metode ini construct_finder_sql(options)( lib/active_record/base.rb:1681) harus Anda gunakan sendkarena bersifat pribadi.

Edit : construct_finder_sql dihapus di Rails 5.1.0.beta1 .

John F. Miller
sumber
1
Ini dekat dengan apa yang saya pikirkan. Saya kira seseorang dapat menulis plugin yang akan melakukan sesuatu seperti: SampleModel.find (: all,: select => "DISTINCT (*)" date,: condition => [" > # {self.date}"],: limit => 1 ,: order => ' date',: group => " date") .show_generated_sql dan panggil ini metode construct_finder_sql.
rswolff
1
Dengan DataMapper Anda dapat melakukannya karena tidak langsung menjalankan kueri. ActiveRecord di sisi lain mengeksekusi kueri dengan segera. show_generated_sqlakan bekerja pada kumpulan data yang sudah diambil dari find.
John F. Miller
4
In Rails 3 construct_finder_sqlmemang dihapus
Veger
190

Mirip dengan penger, tetapi bekerja kapan saja di konsol bahkan setelah kelas dimuat dan logger telah di-cache:

Untuk Rails 2:

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

Untuk Rails 3.0.x:

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

Untuk Rails> = 3.1.0, ini sudah dilakukan secara default di konsol. Jika terlalu berisik dan Anda ingin mematikannya, Anda dapat melakukan:

ActiveRecord::Base.logger = nil
gtd
sumber
Ini tidak berhasil untuk saya rails console. Apakah ini hanya berfungsi untuk irb yang dimuat langsung dari shell? (atau dipindahkan untuk rel 3?)
Eric Hu
2
Mereka memindahkannya ke tempat yang lebih masuk akal untuk Rails 3 ... lihat versi terbaru saya.
gtd
Apakah ada cara untuk melakukan ini secara otomatis setiap kali saya memulai konsol? Sesuatu seperti hook sebelum memuat?
streaming7
1
@ stream7..Saya tidak tahu apakah Anda memerlukan ini sekarang, tetapi Anda dapat memindahkan kode ini ke environment.rb.. .. if "irb" == $0;ActiveRecord::Base.logger = Logger.new(STDOUT);enddapatkan ini dari komentar di http://weblog.jamisbuck.org/2007/1/8/watching-activerecord-do -it-s-thing
rubyprince
Ini tidak menjawab pertanyaan: bagaimana cara menampilkan sql untuk kueri tertentu saat melakukan debug tanpa menjalankan kueri.
bradw2k
87

Tempelkan suatu puts query_object.classtempat untuk melihat jenis objek yang Anda kerjakan, kemudian cari dokumennya.

Misalnya, di Rails 3.0, penggunaan cakupan ActiveRecord::Relationyang memiliki #to_sqlmetode. Sebagai contoh:

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

Kemudian, di suatu tempat Anda dapat melakukan:

puts Contact.frequently_contacted.to_sql
pathdependent
sumber
3
Mengapa jawaban ini tidak diberi suara positif? Untuk Rails 3 ini adalah jawaban yang jauh lebih baik - Anda bisa mendapatkan hasil dari pernyataan AR :: Relation hanya dengan meletakkan ".to_sql" di akhir. Pertanyaan ini juga memberikan jawaban itu: stackoverflow.com/questions/3814738/…
Steve Midgley
5
Hati-hati: Anda tidak akan mendapatkan semua SQL jika Anda memiliki includesdalam relasi Anda
Barry Kelly
3
@BarryKelly Mengapa demikian?
Wisnu Narang
25

Ini mungkin pertanyaan lama tapi saya menggunakan:

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

The explainMetode akan memberikan pernyataan yang cukup SQL rinci tentang apa yang terjadi untuk melakukan

Akshat
sumber
3
Ini juga akan menjalankan kueri, yang mungkin bukan yang diinginkan orang, hanya sebagai catatan.
Ibrahim
22

cukup gunakan to_sqlmetode dan itu akan menampilkan query sql yang akan dijalankan. itu bekerja pada relasi rekaman aktif.

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

ketika melakukannya find, itu tidak akan berfungsi, jadi Anda harus menambahkan id itu secara manual ke kueri atau menjalankannya menggunakan where.

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

Inilah yang biasanya saya lakukan untuk mendapatkan SQL yang dihasilkan di konsol

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

Anda harus melakukan ini saat pertama kali memulai konsol, jika Anda melakukan ini setelah Anda mengetik beberapa kode, sepertinya tidak berfungsi.

Tidak dapat benar-benar mengambil kredit untuk ini, menemukannya lama sekali dari blog seseorang dan tidak dapat mengingat milik siapa.

penger
sumber
1
Saya cukup yakin itu adalah blog Jamis Buck: weblog.jamisbuck.org/2007/1/8/…
rswolff
1
Saya tidak yakin apakah itu karena Rails 2.3 atau sesuatu di lingkungan saya, tetapi ini tidak berhasil untuk saya. Lihat tanggapan saya di bawah.
gtd
Ini adalah solusi yang bagus IMO.
Benjamin Atkin
10

Buat file .irbrc di direktori home Anda dan tempel ini di:

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

Itu akan menampilkan pernyataan SQL ke sesi irb Anda saat Anda pergi.

EDIT: Maaf itu masih akan mengeksekusi kueri, tapi yang paling dekat yang saya tahu.

EDIT: Sekarang dengan arel, Anda dapat membangun cakupan / metode selama objek mengembalikan ActiveRecord :: Relation dan memanggil .to_sql di atasnya dan itu akan mengeluarkan sql yang akan dieksekusi.

nitecoder
sumber
Inilah yang telah saya lakukan, tetapi saya lebih tertarik untuk melihat SQL yang diproyeksikan yang akan dihasilkan kueri ActiveRecord. Saya terkejut tidak ada cara sederhana untuk melakukan ini ...
rswolff
5

Cara khas saya untuk melihat apa yang digunakan sql adalah dengan memperkenalkan "bug" di sql, lalu Anda akan mendapatkan pesan kesalahan yang dimuntahkan ke logger normal (dan layar web) yang memiliki sql tersebut. Tidak perlu mencari ke mana arah stdout ...

rogerdpack
sumber
1
Solusi spektakuler tapi tercepat :)
Ján Janočko
2

Coba plugin show_sql . Plugin memungkinkan Anda untuk mencetak SQL tanpa menjalankannya

SampleModel.sql(:select => "DISTINCT(*)", :conditions => ["`date` > #{self.date}"], :limit => 1, :order => '`date`', :group => "`date`")
Harish Shetty
sumber
1
Sepertinya tautannya rusak (kesalahan 404). Mungkin, Ryan Bigg menghapus plugin tersebut.
DNNX
1

Anda bisa mengubah metode log koneksi untuk memunculkan pengecualian, mencegah kueri dijalankan.

Ini peretasan total, tetapi tampaknya berhasil untuk saya (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
juga
sumber
0

Di Rails 3 Anda dapat menambahkan baris ini ke config / environment / development.rb

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

Namun itu akan mengeksekusi kueri. Tapi setengahnya mendapat jawaban:

Timbinous
sumber