2013-02-12 4 views
145

Как я могу преобразовать этот код в raw sql и использовать в rails? Потому что, когда я развертываю этот код в heroku, возникает ошибка таймаута запроса. Я думаю, что это будет быстрее, если я буду использовать raw sql.Rails 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 будет быстрее? Как вы знаете его проблему SQL? –

+0

Не видя плана запроса, я бы предположил, что проблема, с которой вы сталкиваетесь, - это упорядочить по create_at. Вероятно, вы выполняете seq-сканирование по всем целым таблицам (а также в таблице проектов). Выполнение двух из тех, которые когда-то были методом контроллера на большой таблице и недостаточно мощной БД (базы данных герою настраиваются в целом и относительно недостаточны для потраченного вами $$), вы, скорее всего, получите тайм-ауты. Если вы не делаете много вставок в эти таблицы, вы можете сделать отсортированный индекс: https://devcenter.heroku.com/articles/postgresql-indexes#sorted-indexes –

+1

Ну, всегда было бы быстрее вручную разрезать sql (если вы знаете, что делаете). Rails делает действительно неприятный sql. Он работает хорошо, но, чтобы быть общим, он должен ... быть общим. Тем не менее, Джим, вероятно, прав. Создание индексации было бы лучше. Попробуйте запустить ваш запрос в pgadmin (против вашего db) – baash05

ответ

281

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

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

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

+38

примечание стороны: 'records_array' будет разных типов для разных адаптеров базы данных. Если вы используете PG, это будет экземпляр 'PG :: Result', а не' Array'. – tybro0103

+42

, а затем вам нужно будет вызвать 'values' в этом объекте' PG :: Result', чтобы получить массив результатов – jobwat

+3

ПРЕДУПРЕЖДЕНИЕ DEPRECATION: #connection устарела в пользу доступа к ней через класс. – baash05

24

Вы можете сделать прямой 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, подготовить и exec_prepared, как описано в https://stackoverflow.com/a/13806512/178651

Вы также можете поместить исходные фрагменты 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 и payments_errors, а затем имеет модель для представления (которая будет доступна только для чтения). Некоторые БД поддерживают материализованные представления, что может быть хорошей идеей для производительности.

Также рассмотрите аппаратные или виртуальные спецификации на сервере Rails и сервере БД, конфигурации, дискового пространства, скорости сети/времени ожидания и т. Д., Близости и т. Д. И рассмотрите возможность размещения БД на разных серверах/виртуальных машинах, чем приложение Rails, если вы «т, и т.д.

+0

Поскольку сортировщик сортирует по дате, я думаю, что разбиение на страницы не поможет? Я не самый глубокий на SQL, но я понимаю, что запрос в исходном столбце должен будет извлечь всю таблицу, чтобы отсортировать ее - только тогда она может ограничить результаты. Я подозреваю, что основная часть плана запроса находится в предложении order. –

+0

@JimWrubel уточнил абзац о разбиении на страницы - формулировка была немного не в своем роде. –

+0

Отличное редактирование. Мне нравится идея модели payment_records. –

107

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

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 - Findinig by SQL.
  2. Ruby on Rails - Active Record Result .
+4

'# find_by_sql' именно то, что я хотел, большое вам спасибо. – Shelvacu

+0

Очень полезно! Спасибо. – BrunoFacca

+0

'find_by_sql' работает! милая! – Mustafa

11

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