Làm cách nào tôi có thể xem SQL sẽ được tạo bởi một truy vấn ActiveRecord nhất định trong Ruby on Rails


116

Tôi muốn xem câu lệnh SQL mà một Truy vấn ActiveRecord nhất định sẽ tạo. Tôi nhận ra rằng tôi có thể lấy thông tin này từ nhật ký sau khi truy vấn đã được đưa ra, nhưng tôi đang tự hỏi liệu có phương thức nào có thể được gọi trên và Truy vấn ActiveRecord không.

Ví dụ:

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

Tôi muốn mở bảng điều khiển irb và tìm một phương thức ở cuối sẽ hiển thị SQL mà truy vấn này sẽ tạo ra, nhưng không nhất thiết phải thực thi truy vấn.

Câu trả lời:


12

Lần cuối cùng tôi cố gắng làm điều này, không có cách chính thức nào để làm điều đó. Tôi đã sử dụng chức năng mà findbạn bè của nó sử dụng để tạo các truy vấn của họ trực tiếp. Nó là API riêng nên có nguy cơ rất lớn là Rails 3 sẽ hoàn toàn phá vỡ nó, nhưng để gỡ lỗi, đó là một giải pháp tốt.

Phương thức là construct_finder_sql(options)( lib/active_record/base.rb:1681) bạn sẽ phải sử dụng sendvì nó là riêng tư.

Chỉnh sửa : construct_finder_sql đã bị xóa trong Rails 5.1.0.beta1 .


1
Điều này gần với những gì tôi đang nghĩ. Tôi đoán một người có thể viết một plugin có tác dụng như: SampleModel.find (: all,: select => "DISTINCT (*)" date,: condition => [" > # {self.date}"],: limit => 1 ,: order => ' date',: group => " date") .show_generated_sql và gọi phương thức construct_finder_sql.
rswolff

1
Với DataMapper, bạn có thể vì nó không chạy truy vấn ngay lập tức. Mặt khác, ActiveRecord thực hiện truy vấn ngay lập tức. show_generated_sqlsẽ hoạt động trên một tập dữ liệu đã được truy xuất từ find.
John F. Miller

4
Trong Rails 3 construct_finder_sqlthực sự bị loại bỏ
Veger

190

Tương tự như penger's, nhưng hoạt động bất cứ lúc nào trong bảng điều khiển ngay cả sau khi các lớp đã được tải và trình ghi nhật ký đã được lưu vào bộ nhớ cache:

Đối với Rails 2:

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

Đối với Rails 3.0.x:

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

Đối với Rails> = 3.1.0, điều này đã được thực hiện theo mặc định trong bảng điều khiển. Trong trường hợp quá ồn và bạn muốn tắt nó đi, bạn có thể làm:

ActiveRecord::Base.logger = nil

Điều này không làm việc cho tôi trong rails console. Nó chỉ hoạt động cho irb được tải trực tiếp từ shell? (hay nó đã bị xóa cho rails 3?)
Eric Hu

2
Họ đã chuyển nó đến một nơi hợp lý hơn cho Rails 3 ... hãy xem phiên bản cập nhật của tôi.
gtd

Có cách nào để thực hiện việc này tự động mỗi khi tôi khởi động bảng điều khiển không? Một cái gì đó giống như một móc tải trước?
stream7

1
@ stream7..Tôi không biết bây giờ bạn có cần cái này hay không, nhưng bạn có thể chuyển mã này sang environment.rb.. .. if "irb" == $0;ActiveRecord::Base.logger = Logger.new(STDOUT);endlấy mã này từ các nhận xét trong http://weblog.jamisbuck.org/2007/1/8/watching-activerecord-do -nó-s-điều
rubyprince

Điều này không trả lời câu hỏi: làm thế nào để hiển thị sql cho một truy vấn cụ thể trong khi gỡ lỗi mà không cần chạy truy vấn.
bradw2k

87

Dán vào một puts query_object.classnơi nào đó để xem loại đối tượng bạn đang làm việc, sau đó tra cứu tài liệu.

Ví dụ, trong Rails 3.0, phạm vi sử dụng ActiveRecord::Relationcó một #to_sqlphương thức. Ví dụ:

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

Sau đó, bạn có thể làm ở đâu đó:

puts Contact.frequently_contacted.to_sql

3
Tại sao câu trả lời này không được ủng hộ? Đối với Rails 3, đó là một cách trả lời tốt hơn - bạn có thể nhận được kết quả của bất kỳ câu lệnh AR :: Relation nào chỉ bằng cách đặt ".to_sql" ở cuối. Câu hỏi này cũng cung cấp câu trả lời rằng: stackoverflow.com/questions/3814738/...
Steve Midgley

5
Cẩn thận: bạn sẽ không có được tất cả các SQL nếu bạn có một includestrong mối quan hệ của bạn
Barry Kelly

3
@BarryKelly Tại sao vậy?
Vishnu Narang

25

Đây có thể là một câu hỏi cũ nhưng tôi sử dụng:

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

Các explainphương pháp sẽ cho khá một câu lệnh SQL chi tiết về những gì xảy ra của nó để làm


3
Nó cũng sẽ chạy truy vấn, có thể không phải những gì mọi người muốn, chỉ để ghi lại.
Ibrahim

22

chỉ cần sử dụng to_sqlphương thức và nó sẽ xuất ra truy vấn sql sẽ được chạy. nó hoạt động trên một quan hệ bản ghi đang hoạt động.

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

khi thực hiện find, nó sẽ không hoạt động, vì vậy bạn sẽ cần thêm id đó theo cách thủ công vào truy vấn hoặc chạy nó bằng cách sử dụng ở đâu.

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

11

Đây là những gì tôi thường làm để tạo SQL trong bảng điều khiển

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

Bạn phải thực hiện việc này khi mới khởi động bảng điều khiển, nếu bạn làm điều này sau khi nhập một số mã, nó có vẻ không hoạt động

Không thể thực sự ghi công cho điều này, đã tìm thấy nó từ lâu trong blog của ai đó và không thể nhớ nó là của ai.


1
Tôi khá chắc chắn đó là blog của Jamis Buck: weblog.jamisbuck.org/2007/1/8/…
rswolff

1
Tôi không chắc đó là do Rails 2.3 hay thứ gì đó trong môi trường của tôi, nhưng điều này không hiệu quả với tôi. Xem phản hồi của tôi bên dưới.
gtd

Đây là một giải pháp tuyệt vời IMO.
Benjamin Atkin

10

Tạo tệp .irbrc trong thư mục chính của bạn và dán tệp này vào:

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

Điều đó sẽ xuất các câu lệnh SQL vào phiên irb của bạn khi bạn thực hiện.

CHỈNH SỬA: Xin lỗi vì điều đó sẽ vẫn thực thi truy vấn, nhưng nó gần nhất mà tôi biết.

CHỈNH SỬA: Bây giờ với arel, bạn có thể xây dựng phạm vi / phương thức miễn là đối tượng trả về ActiveRecord :: Relation và gọi .to_sql trên đó và nó sẽ đưa ra sql sẽ được thực thi.


Đây là những gì tôi đang làm, nhưng tôi quan tâm hơn đến việc chỉ cần nhìn thấy SQL dự kiến ​​mà truy vấn ActiveRecord sẽ tạo ra. Tôi ngạc nhiên không có cách nào đơn giản để làm điều này ...
rswolff

5

Cách điển hình của tôi để xem nó sử dụng sql gì là giới thiệu một "lỗi" trong sql, sau đó bạn sẽ nhận được thông báo lỗi được đưa ra trình ghi nhật ký bình thường (và màn hình web) có sql được đề cập. Không cần phải tìm xem stdout sẽ đi đâu ...


1
Giải pháp ngoạn mục nhưng nhanh nhất :)
Ján Janočko

2

Hãy thử plugin show_sql . Plugin cho phép bạn in SQL mà không cần chạy nó

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

1
Có vẻ như liên kết đã bị hỏng (lỗi 404). Có thể, Ryan Bigg đã xóa plugin.
DNNX

1

Bạn có thể thay đổi phương thức nhật ký của kết nối để tạo ra một ngoại lệ, ngăn không cho truy vấn chạy.

Đó là một cuộc tấn công hoàn toàn, nhưng nó có vẻ hiệu quả với tôi (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

Trong Rails 3, bạn có thể thêm dòng này vào cấu hình / môi trường / phát triển.rb

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

Tuy nhiên, nó sẽ thực hiện truy vấn. Nhưng một nửa được trả lời:

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.