Przykład Rails raw SQL

Jak mogę przekonwertować ten kod na surowy sql i użyć go w rails? Ponieważ kiedy wdrażam ten kod w heroku, pojawia się błąd limitu czasu żądania.Myślę, że będzie to szybsze, jeśli użyję surowego sql.

@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)
Author: Dorian, 2013-02-12

5 answers

Możesz to zrobić:

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

records_array będzie to wynik twojego zapytania sql w tablicy, którą możesz iterować.

 313
Author: Huy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-12 19:48:23

Wiem, że to stare... Ale dzisiaj miałem ten sam problem i znalazłem rozwiązanie:

Model.find_by_sql

Jeśli chcesz utworzyć instancję wyników:

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

Jeśli chcesz tylko hash wartości:

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

Result object:

select_all zwraca obiekt result. Możesz z nim robić magiczne rzeczy.

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

Źródła:

  1. ActiveRecord-Findig by SQL .
  2. Ruby on Rails-Active Record Result .
 127
Author: João Paulo Motta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-17 20:14:58

Można zrobić direct SQL, aby mieć jedno zapytanie dla obu tabel. Podam przykład sanitized query, aby mieć nadzieję, że ludzie nie umieszczają zmiennych bezpośrednio w samym łańcuchu znaków (SQL injection danger), mimo że ten przykład nie precyzował potrzeby:

@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 : Jak powiedział Huy, prosty sposób to ActiveRecord::Base.connection.execute("..."). Innym sposobem jest ActiveRecord::Base.connection.exec_query('...').rows. I możesz używać natywnych instrukcji prepared, np. jeśli używasz postgres, prepared można wykonać za pomocą raw_connection, prepare, i exec_prepared zgodnie z opisem w https://stackoverflow.com/a/13806512/178651

Możesz także wstawiać surowe fragmenty SQL do zapytań relacyjnych ActiveRecord: http://guides.rubyonrails.org/active_record_querying.html oraz w stowarzyszeniach, zakresach itp. Prawdopodobnie możesz skonstruować ten sam SQL za pomocą zapytań relacyjnych ActiveRecord i możesz robić fajne rzeczy z Arelem, o czym wspomina Ernie w http://erniemiller.org/2010/03/28/advanced-activerecord-3-queries-with-arel / . i, oczywiście istnieją inne Ormy, klejnoty itp.

Jeśli to ma być często używane i dodawanie indeksów nie spowoduje innych problemów z wydajnością / zasobami, rozważ dodanie indeksu w DB dla payment_details.created_at i dla payment_errors.created_at.

Jeśli wiele rekordów, a nie wszystkie rekordy muszą pojawić się na raz, rozważ użycie paginacja:

Jeśli potrzebujesz paginate, rozważ utworzenie widoku w DB najpierw o nazwie payment_records, który łączy tabele payment_details i payment_errors, a następnie mieć model dla widoku (który będzie tylko do odczytu). Niektóre DBs obsługują zmaterializowane widoki, co może być dobrym pomysłem na wydajność.

Rozważmy również sprzęt lub specyfikacje VM na serwerze Rails i serwerze DB, konfiguracja, miejsce na dysku, prędkość sieci/opóźnienie / itp., bliskość itp. I rozważ umieszczenie DB na innym serwerze / maszynie wirtualnej niż aplikacja Rails, jeśli tego nie zrobiłeś itp.

 24
Author: Gary S. Weaver,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:40

Możesz wykonać surowe zapytanie używając ActiveRecord. I zaproponuję, aby przejść z bloku 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)
 16
Author: Deepak Mahakale,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-20 14:13:47

Możesz również mieszać surowy SQL z warunkami ActiveRecord, na przykład, jeśli chcesz wywołać funkcję w warunku:

my_instances = MyModel.where.not(attribute_a: nil) \
  .where('crc32(attribute_b) = ?', slot) \
  .select(:id)
 1
Author: tsauerwein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-02 09:30:46