Ответ 1
Вы можете сделать это:
sql = "Select * from ... your sql query here"
records_array = ActiveRecord::Base.connection.execute(sql)
records_array
будет результатом вашего запроса sql в массиве, который вы можете выполнить итерацией.
Как я могу преобразовать этот код в raw sql и использовать в rails? Потому что, когда я развертываю этот код в heroku, возникает ошибка таймаута запроса. Я думаю, что это будет быстрее, если я использую raw 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)
Вы можете сделать это:
sql = "Select * from ... your sql query here"
records_array = ActiveRecord::Base.connection.execute(sql)
records_array
будет результатом вашего запроса sql в массиве, который вы можете выполнить итерацией.
Я знаю, что это старый... Но у меня была та же проблема сегодня и нашел решение:
Model.find_by_sql
Если вы хотите создать экземпляр результатов:
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
Если вы просто хотите хэш значений:
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"}
]
Объект результата:
select_all
возвращает объект result
. Вы можете делать магические вещи с ним.
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
Источники:
Вы можете сделать прямой SQL, чтобы иметь один запрос для обеих таблиц. Я предоставлю пример из санитарного запроса, который, надеюсь, заставит людей помещать переменные непосредственно в саму строку (опасность инъекции SQL), хотя этот пример не указал на необходимость:
@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
Изменить: как сказал Хуй, простой способ ActiveRecord::Base.connection.execute("...")
. Другой способ - ActiveRecord::Base.connection.exec_query('...').rows
. И вы можете использовать собственные подготовленные операторы, например. если вы используете postgres, подготовленный оператор может быть выполнен с помощью raw_connection, prepare и exec_prepared, как описано в fooobar.com/questions/44720/...
Вы также можете поместить необработанные фрагменты SQL в реляционные запросы ActiveRecord: http://guides.rubyonrails.org/active_record_querying.html и в ассоциациях, областях и т.д. Возможно, вы могли бы построить тот же SQL-запрос с реляционными запросами ActiveRecord и можете делать классные вещи с помощью ARel, поскольку Эрни упоминает в http://erniemiller.org/2010/03/28/advanced-activerecord-3-queries-with-arel/. И, конечно, есть другие ORM, драгоценные камни и т.д.
Если это будет использоваться много, и добавление индексов не вызовет других проблем с производительностью/ресурсами, подумайте над добавлением индекса в базу данных для payment_details.created_at и для payment_errors.created_at.
Если требуется много записей и не все записи, рассмотрите возможность использования разбивки на страницы:
Если вам нужно разбивать страницы, подумайте о создании представления в БД, сначала называемом payment_records, который объединяет таблицы payments_details и payment_errors, а затем имеет модель для представления (которая будет доступна только для чтения). Некоторые БД поддерживают материализованные представления, что может быть хорошей идеей для производительности.
Также рассмотрите аппаратные или виртуальные спецификации на сервере Rails и сервере БД, конфигурации, дискового пространства, скорости сети/времени ожидания и т.д., близости и т.д. И рассмотрите возможность размещения БД на разных серверах/виртуальных машинах, чем приложение Rails, t и т.д.
Вы можете выполнить необработанный запрос, используя ActiveRecord
. И я предлагаю пойти с блоком 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)
Вы также можете смешивать сырой SQL с условиями ActiveRecord, например, если вы хотите вызвать функцию в условии:
my_instances = MyModel.where.not(attribute_a: nil) \
.where('crc32(attribute_b) = ?', slot) \
.select(:id)
Я хочу работать с exec_query
класса ActiveRecord
, потому что он возвращает отображение запроса, превращающегося в объект, поэтому становится очень практичным и продуктивным выполнять итерации с объектами, когда объектом является Raw SQL.
Пример:
values = ActiveRecord::Base.connection.exec_query("select * from clients")
p values
и вернуть этот полный запрос:
[{"id": 1, "name": "user 1"}, {"id": 2, "name": "user 2"}, {"id": 3, "name": "user 3"}]
Получить только список значений
p values.rows
[[1, "user 1"], [2, "user 2"], [3, "user 3"]]
Получить только поля столбцов
p values.columns
["id", "name"]