Rails raw SQL örneği


239

Bu kodu ham sql'e nasıl dönüştürebilir ve raylarda kullanabilirim? Çünkü bu kodu heroku'da dağıttığımda, bir istek zaman aşımı hatası var.

@payments = PaymentDetail.joins(:project).order('payment_details.created_at desc')
@payment_errors = PaymentError.joins(:project).order('payment_errors.created_at desc')

@all_payments = (@payments + @payment_errors)

3
Neden ham SQL'in daha hızlı olacağını düşünüyorsunuz? Bunun bir SQL sorunu olduğunu nereden biliyorsunuz?
John Naegle

Sorgu planını görmeden yaşadığınız sorun yaratılmış_at tarafından sipariş olduğunu tahmin ediyorum. Muhtemelen bu tabloların tamamında bir seq taraması yapıyorsunuz (oh ve proje tablosunu da getiriyorsunuz). Bunlardan ikisini bir kerede büyük bir masada ve düşük güçte DB'de (heroku veritabanları genel olarak ayarlanır ve harcadığınız $$ için nispeten az güçlendirilir) zaman aşımına uğrayabilirsiniz. Bu tablolara çok fazla ekleme yapmıyorsanız sıralı bir dizin yapabilirsiniz: devcenter.heroku.com/articles/postgresql-indexes#sorted-indexes
Jim Wrubel

1
Peki sql'i el ile kesmek her zaman daha hızlı olurdu (ne yaptığını biliyorsanız). Raylar gerçekten kötü bir sql yapar. İyi çalışıyor ama genel olmak için ... genel olmak zorunda. Jim büyük olasılıkla haklıydı .. endeksleme oluşturmak daha iyi olurdu dedi. Sorgunuzu pgadmin (db'nize karşı) olarak çalıştırmayı deneyin
baash05

Yanıtlar:


427

Bunu yapabilirsiniz:

sql = "Select * from ... your sql query here"
records_array = ActiveRecord::Base.connection.execute(sql)

records_array daha sonra yineleyebileceğiniz bir dizideki sql sorgunuzun sonucu olur.


52
yan not: records_arrayfarklı veritabanı adaptörleri için farklı tiplerde olacaktır. PG kullanıyorsanız, bunun bir örneği olacaktır PG::Result, değil Array.
tybro0103

58
ve sonuç dizisini elde etmek için valuesbu PG::Resultnesneyi çağırmanız gerekir
jobwat

3
@ baash05 "Sınıf aracılığıyla erişmek lehine" ne demek istiyorsun. - Bu, bir model olmadan masaya nasıl erişileceğinden bahsediyor
Yo Ludke

1
Aslında OP'de bir model kullanmamaya dair hiçbir söz yok. Ham sql nasıl çalıştırılır. PaymentDetail.execute (sql) çalışır.
baash05

20
Sevdiğim ActiveRecord::Base.connection.exec_querybir döndürdüğü için daha iyi ActiveRecords::Resultgibi kullanışlı yöntemleri vardır nesne .columnsve .rowserişim başlıkları ve değerlere. Bir karma listesi .executeile başa çıkmak için sorun olabilir ve bana bir SUM GROUP BY yan tümcesi ile koştu zaman gereksiz sonuçlar verdi.
Francio Rodrigues

181

Bunun eski olduğunu biliyorum ... Ama bugün aynı sorunu yaşıyordum ve bir çözüm buldum:

Model.find_by_sql

Sonuçları somutlaştırmak istiyorsanız:

Client.find_by_sql("
  SELECT * FROM clients
  INNER JOIN orders ON clients.id = orders.client_id
  ORDER BY clients.created_at desc
")
# => [<Client id: 1, first_name: "Lucas" >, <Client id: 2, first_name: "Jan">...]

Model.connection.select_all('sql').to_hash

Sadece değerlerin bir karmasını istiyorsanız:

Client.connection.select_all("SELECT first_name, created_at FROM clients
   WHERE id = '1'").to_hash
# => [
  {"first_name"=>"Rafael", "created_at"=>"2012-11-10 23:23:45.281189"},
  {"first_name"=>"Eileen", "created_at"=>"2013-12-09 11:22:35.221282"}
]

Sonuç nesnesi:

select_all döndürür result nesneyi . Onunla sihirli şeyler yapabilirsiniz.

result = Post.connection.select_all('SELECT id, title, body FROM posts')
# Get the column names of the result:
result.columns
# => ["id", "title", "body"]

# Get the record values of the result:
result.rows
# => [[1, "title_1", "body_1"],
      [2, "title_2", "body_2"],
      ...
     ]

# Get an array of hashes representing the result (column => value):
result.to_hash
# => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
      {"id" => 2, "title" => "title_2", "body" => "body_2"},
      ...
     ]

# ActiveRecord::Result also includes Enumerable.
result.each do |row|
  puts row['title'] + " " + row['body']
end

Kaynaklar:

  1. ActiveRecord - SQL tarafından Findinig .
  2. Ruby on Rails - Aktif Kayıt Sonucu .

9
#find_by_sqltam olarak istediğim şey, çok teşekkür ederim.
Shelvacu

#find_by_sql öyle !!
Steven L.10

28

Kullanarak ham sorguyu yürütebilirsiniz ActiveRecord. Ve SQL bloğu ile gitmenizi önereceğim

query = <<-SQL 
  SELECT * 
  FROM payment_details
  INNER JOIN projects 
          ON projects.id = payment_details.project_id
  ORDER BY payment_details.created_at DESC
SQL

result = ActiveRecord::Base.connection.execute(query)

Merhaba .. Bu sorguya parametre geçirmek için herhangi bir yolu var mı?
Akshay Goyal

@AkshayGoyal blok içinde normal yakut string enterpolasyonu kullanabilirsiniz. SELECT * FROM users WHERE users.id = #{ user.id }
Nathan Beck

2
Bunu yaparken SQL enjeksiyon olasılığına maruz
bırakabilirsiniz

26

Her iki tablo için tek bir sorguya sahip olmak için doğrudan SQL yapabilirsiniz. Bu örnek için ihtiyacı belirtmedi olsa bile, insanların değişkenleri doğrudan dizenin içine (SQL enjeksiyon tehlikesi) koyarak umarım temizlenmiş bir sorgu örneği sağlayacaktır:

@results = []
ActiveRecord::Base.connection.select_all(
  ActiveRecord::Base.send(:sanitize_sql_array, 
   ["... your SQL query goes here and ?, ?, ? are replaced...;", a, b, c])
).each do |record|
  # instead of an array of hashes, you could put in a custom object with attributes
  @results << {col_a_name: record["col_a_name"], col_b_name: record["col_b_name"], ...}
end

Düzenleme : Huy'un dediği gibi, basit bir yol ActiveRecord::Base.connection.execute("..."). Başka bir yol ActiveRecord::Base.connection.exec_query('...').rows. Ve yerel hazırlanmış ifadeler kullanabilirsiniz, örneğin postgres kullanılıyorsa hazırlanmış deyim, htt_://stackoverflow.com/a/13806512/178651 adresinde açıklandığı gibi raw_connection, ready_prepared ile yapılabilir.

Ham SQL parçalarını ActiveRecord ilişkisel sorgularına da koyabilirsiniz: http://guides.rubyonrails.org/active_record_querying.html ve ilişkilendirmelerde, kapsamlarda vb. Muhtemelen aynı SQL'i ActiveRecord ilişkisel sorgularıyla oluşturabilir ve Ernie olarak ARel, http://erniemiller.org/2010/03/28/advanced-activerecord-3-queries-with-arel/ adresinde bahsedilmektedir . Ve elbette başka ORM'ler, taşlar vb.

Bu çok kullanılacaksa ve endeks eklemek diğer performans / kaynak sorunlarına neden olmazsa, DB'ye payment_details.created_at ve payment_errors.created_at için bir dizin eklemeyi düşünün.

Tüm kayıtların aynı anda gösterilmesi gerekmiyorsa, sayfa numaralandırmayı kullanmayı düşünün:

Sayfalandırmanız gerekirse, önce DB'de payment_details ve payment_errors tablolarını birleştiren bir görünüm oluşturmayı ve ardından görünüm için bir model oluşturmayı (salt okunur olacak) düşünün. Bazı DB'ler performans için iyi bir fikir olabilecek materyalize görünümleri destekler.

Ayrıca Rails sunucusu ve DB sunucusu, yapılandırma, disk alanı, ağ hızı / gecikme / vb., Yakınlık, vb. Donanım veya VM özelliklerini de göz önünde bulundurun. .


Asker tarihe göre sıralama yaptığı için sayfalandırma işe yaramaz mı? Ben SQL derin değilim ama benim anlayış orijinal yazı sorgu sıralamak için tüm tablo getirmesi gerekir - ancak o zaman sonuçları sınırlayabilir. Sorgu planı toplu sipariş yan tümcesinde olduğundan şüpheleniyorum.
Jim Wrubel

@JimWrubel sayfalandırma konusundaki paragrafı açıklığa kavuşturdu; ifadeler biraz farklıydı.
Gary S. Weaver

2

Nesneye dönüştüren sorgunun eşlemesini döndürdüğü exec_queryiçin ActiveRecordsınıfla çalışmak istiyorum , bu yüzden konu Raw SQL olduğunda nesnelerle yinelemek için çok pratik ve üretken olur.

Misal:

values = ActiveRecord::Base.connection.exec_query("select * from clients")
p values

ve şu tam sorguyu döndürün:

[{"id": 1, "name": "user 1"}, {"id": 2, "name": "user 2"}, {"id": 3, "name": "user 3"}]

Yalnızca değerlerin listesini almak için

p values.rows

[[1, "user 1"], [2, "user 2"], [3, "user 3"]]

Yalnızca alan sütunlarını almak için

p values.columns

["id", "name"]

0

Ham SQL'i ActiveRecord koşullarıyla da karıştırabilirsiniz, örneğin bir durumda bir işlevi çağırmak istiyorsanız:

my_instances = MyModel.where.not(attribute_a: nil) \
  .where('crc32(attribute_b) = ?', slot) \
  .select(:id)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.