Comment puis-je convertir ce code en sql brut et l'utiliser dans des rails? Parce que lorsque je déploie ce code dans heroku, il se produit une erreur de délai de requête. Je pense que cela sera plus rapide si j'utilise du SQL brut.
@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)
Tu peux le faire:
sql = "Select * from ... your sql query here"
records_array = ActiveRecord::Base.connection.execute(sql)
records_array
serait alors le résultat de votre requête SQL dans un tableau que vous pouvez parcourir.
Je sais que c'est vieux ... Mais j'avais le même problème aujourd'hui et je trouvais une solution:
Model.find_by_sql
Si vous voulez instancier les résultats:
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
Si vous voulez juste un hachage de valeurs:
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"}
]
Objet de résultat:
select_all
renvoie un objet result
. Vous pouvez faire des choses magiques avec elle.
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
Sources:
Vous pouvez faire du SQL direct pour avoir une requête unique pour les deux tables. Je vais fournir un exemple de requête assainie pour empêcher les gens d'insérer des variables directement dans la chaîne elle-même (danger d'injection SQL), même si cet exemple n'en spécifiait pas la nécessité:
@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
Edit: comme Huy l'a dit, un moyen simple est ActiveRecord::Base.connection.execute("...")
. Une autre façon est ActiveRecord::Base.connection.exec_query('...').rows
. Et vous pouvez utiliser des instructions préparées natives, par exemple Si vous utilisez postgres, une instruction préparée peut être réalisée avec raw_connection, prepare et exec_prepared comme décrit dans https://stackoverflow.com/a/13806512/178651
Vous pouvez également insérer des fragments SQL bruts dans les requêtes relationnelles ActiveRecord: http://guides.rubyonrails.org/active_record_querying.html Ainsi que dans des associations, des portées, etc. Vous pouvez probablement construire le même code SQL avec ActiveRecord relationnel et peut faire des choses cool avec ARel comme Ernie le mentionne dans http://erniemiller.org/2010/03/28/advanced-activerecord-3-queries-with-arel/ . Et, bien sûr, il y a d'autres ORM, gemmes, etc.
Si cela doit être beaucoup utilisé et que l'ajout d'index ne causera pas d'autres problèmes de performance/ressources, envisagez d'ajouter un index dans la base de données pour payment_details.created_at et payment_errors.created_at.
Si vous devez afficher plusieurs enregistrements en même temps, utilisez la pagination:
Si vous avez besoin de paginer, envisagez de créer une vue dans la base de données d’abord appelée payment_records qui combine les tables payment_details et payment_errors, puis créez un modèle pour la vue (en lecture seule). Certaines bases de données prennent en charge les vues matérialisées, ce qui peut être une bonne idée de performance.
Prenez également en compte les spécifications matérielles ou VM sur le serveur Rails et le serveur de base de données, la configuration, l’espace disque, la vitesse/latence du réseau/etc., la proximité, etc. Et envisagez de placer la base de données sur un serveur/une machine virtuelle différents de l’application Rails t, etc.
Vous pouvez exécuter une requête brute en utilisant ActiveRecord
. Et je vais suggérer d'aller avec le bloc SQL
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)
Vous pouvez également mélanger du SQL brut avec des conditions ActiveRecord, par exemple si vous souhaitez appeler une fonction dans une condition:
my_instances = MyModel.where.not(attribute_a: nil) \
.where('crc32(attribute_b) = ?', slot) \
.select(:id)
Je souhaite travailler avec exec_query
de la classe ActiveRecord
, car elle retourne le mappage de la requête qui se transforme en objet. Il est donc très pratique et productif de pouvoir itérer avec les objets lorsque le sujet est du SQL brut.
Exemple:
values = ActiveRecord::Base.connection.exec_query("select * from clients")
p values
et renvoyer cette requête complète:
[{"id": 1, "name": "user 1"}, {"id": 2, "name": "user 2"}, {"id": 3, "name": "user 3"}]
Pour obtenir uniquement une liste de valeurs
p values.rows
[[1, "user 1"], [2, "user 2"], [3, "user 3"]]
Pour obtenir uniquement des champs de colonnes
p values.columns
["id", "name"]