web-dev-qa-db-fra.com

Exemple de SQL brut Rails

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)
184
Johnny Cash

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.

342
Huy

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:

  1. ActiveRecord - Recherche par SQL .
  2. Ruby on Rails - Résultat d'enregistrement actif .
140
João Paulo Motta

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.

24
Gary S. Weaver

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)
21
Deepak Mahakale

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)
0
tsauerwein

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"]
0
Darlan Dieterich