Пример Rails raw 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)

Ответ 1

Вы можете сделать это:

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

records_array будет результатом вашего запроса sql в массиве, который вы можете выполнить итерацией.

Ответ 2

Я знаю, что это старый... Но у меня была та же проблема сегодня и нашел решение:

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

Источники:

  1. ActiveRecord - поиск по SQL.
  2. Ruby on Rails - результат активной записи.

Ответ 3

Вы можете сделать прямой 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 и т.д.

Ответ 4

Вы можете выполнить необработанный запрос, используя 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)

Ответ 5

Вы также можете смешивать сырой SQL с условиями ActiveRecord, например, если вы хотите вызвать функцию в условии:

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

Ответ 6

Я хочу работать с 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"]