webentwicklung-frage-antwort-db.com.de

Rails-Raw-SQL-Beispiel

Wie kann ich diesen Code in RAW-SQL konvertieren und in Schienen verwenden? Wenn ich diesen Code in Heroku implementiere, gibt es einen Anforderungszeitüberschreitungsfehler.

@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

Du kannst das:

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

records_array wäre dann das Ergebnis Ihrer SQL-Abfrage in einem Array, das Sie durchlaufen können.

342
Huy

Ich weiß, das ist alt ... Aber ich hatte heute das gleiche Problem und finde eine Lösung:

Model.find_by_sql

Wenn Sie die Ergebnisse instanziieren möchten:

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

Wenn Sie nur Hashwerte wünschen:

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"}
]

Ergebnisobjekt:

select_all gibt ein result-Objekt zurück. Sie können magische Dinge damit machen.

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

Quellen:

  1. ActiveRecord - Findinig von SQL .
  2. Ruby on Rails - Ergebnis aktiver Datensätze .
140

Sie können direkt SQL ausführen, um eine einzige Abfrage für beide Tabellen zu erhalten. Ich werde ein Beispiel für ein saniertes Abfragebeispiel liefern, um zu verhindern, dass Leute Variablen direkt in den String selbst einfügen (Gefahr durch SQL-Injection), auch wenn in diesem Beispiel nicht die Notwendigkeit dafür angegeben wurde:

@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: Wie Huy sagte, ist ActiveRecord::Base.connection.execute("...") ein einfacher Weg. Ein anderer Weg ist ActiveRecord::Base.connection.exec_query('...').rows. Und Sie können native vorbereitete Anweisungen verwenden, z. Bei Verwendung von Postgres können vorbereitete Anweisungen mit raw_connection, prepare und exec_präpared wie in https://stackoverflow.com/a/13806512/178651 beschrieben ausgeführt werden.

Sie können auch rohe SQL-Fragmente in relationalen ActiveRecord-Abfragen einfügen: http://guides.rubyonrails.org/active_record_querying.html Und in Assoziationen, Bereichen usw. Sie können wahrscheinlich die gleiche SQL-Klasse mit erstellen ActiveRecord relationale Abfragen und kann mit ARel coole Sachen machen, wie Ernie in http://erniemiller.org/2010/03/28/advanced-activerecord-3-queries-with-arel/ erwähnt. Und natürlich gibt es noch andere ORMs, Edelsteine ​​usw.

Wenn dies häufig verwendet wird und das Hinzufügen von Indizes keine anderen Leistungs-/Ressourcenprobleme verursacht, sollten Sie in der Datenbank einen Index für payment_details.created_at und für payment_errors.created_at hinzufügen.

Wenn viele Datensätze und nicht alle Datensätze gleichzeitig angezeigt werden müssen, sollten Sie die Verwendung von Paginierung in Betracht ziehen:

Wenn Sie paginieren müssen, sollten Sie in der DB zuerst eine Sicht namens payment_records erstellen, die die Tabellen payment_details und payment_errors enthält. Dann haben Sie ein Modell für die Sicht (das schreibgeschützt ist). Einige DBs unterstützen materialisierte Ansichten, was für die Leistung eine gute Idee sein kann.

Berücksichtigen Sie auch die Hardware- oder VM - Spezifikationen für Rails-Server und DB-Server, config, Festplattenspeicher, Netzwerkgeschwindigkeit/-latenz usw., Annäherung usw. Außerdem sollten Sie die Datenbank auf einen anderen Server/eine andere VM als die Rails-App setzen, wenn Sie einen Port verwenden nicht usw.

24
Gary S. Weaver

Sie können eine Rohabfrage mit ActiveRecord ausführen. Und ich werde vorschlagen, mit SQL-Block zu gehen 

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

Sie können auch Raw-SQL mit ActiveRecord-Bedingungen mischen, wenn Sie beispielsweise eine Funktion in einer Bedingung aufrufen möchten:

my_instances = MyModel.where.not(attribute_a: nil) \
  .where('crc32(attribute_b) = ?', slot) \
  .select(:id)
0
tsauerwein

Ich möchte mit dem exec_query der ActiveRecord-Klasse arbeiten, da er die Zuordnung der in ein Objekt transformierten Abfrage zurückgibt, sodass es sehr praktisch und produktiv wird, mit den Objekten zu iterieren, wenn das Thema Raw SQL ist.

Beispiel:

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

und diese vollständige Abfrage zurücksenden:

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

Um nur eine Liste von Werten zu erhalten

p values.rows

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

Es werden nur Feldspalten abgerufen

p values.columns

["id", "name"]
0