#sql #ruby-on-rails #rails-activerecord #postgresql-9.1
#sql #ruby-on-rails #rails-activerecord #postgresql-9.1
Вопрос:
Мне нужна помощь в устранении неполадок с запросом к базе данных для получения правильных результатов. Это приложение для фитнеса, разработанное с Rails 4.2.6, Ruby 2.2.4 и базой данных Postgres 1.9.0. Когда пользователь сохраняет новые измерения тела, я пытаюсь отобразить изменения (например, дюймы) с момента последнего измерения. Проблема заключается в том, что значения изменений для ранее сохраненных записей неправильно вычисляются в представлениях show.
Вот что я закодировал до сих пор:
контроллеры /fitness_measurements_controller.rb:
before_action :get_second_latest_waist_measurement
def
get_second_latest_waist_measurement
@waist_second_latest_measurement = @member.fitness_measurements.order(:created_at).offset(1).last.waist || 0
end
fitness_measurements/show.html.erb:
<p>
<strong>Change in waist since last measurement:</strong>
<%= (@fitness_measurement.waist - @waist_second_latest_measurement).round(2) %> in
</p>
С тестовыми данными, вот таблица, которая содержит результаты моего запроса к базе данных. Обратите внимание на значения в «Calc. Изменить столбец».
------------ ----------- ------------------ --------------------
| Date | Waist(in) | Calc. Change(in) | Correct Change(in) |
------------ ----------- ------------------ --------------------
| 2016-10-01 | 37.5 | 1.00 | 1.00 |
------------ ----------- ------------------ --------------------
| 2016-09-01 | 36.5 | 0.00 | 3.00 |
------------ ----------- ------------------ --------------------
| 2016-08-01 | 33.5 | -3.00 | 0.05 |
------------ ----------- ------------------ --------------------
| 2016-07-01 | 33.0 | -3.50 | 0.00 |
------------ ----------- ------------------ --------------------
Как вы видите, за исключением последней сохраненной записи (2016-10-01), значения в «Calc. Столбец «Изменить» неверен. Что-то не так с тем, как я разработал запрос.
В настоящее время мой запрос извлекает запись, которая на 1 ниже последнего сохраненного измерения, как «второе последнее измерение талии». В тестовом примере это запись, созданная в 2016-09-01. Это работает для ПОСЛЕДНЕЙ записи, сохраненной в базе данных (в данном случае 2016-10-01), но выдает неправильные результаты, когда я запрашиваю просмотр страниц ранее сохраненных записей. Например, запись, созданная в 2016-09-01, должна сравниваться с записью, созданной в 2016-08-01.
Использование «offset (1)» в запросе, по-видимому, является корнем моей проблемы, но я не знаю, как получить правильную запись. Есть ли решение для перебора записей? Я не понимаю, как это сделать в этой ситуации?
Как мне исправить запрос к базе данных, чтобы генерировать правильные значения изменений? Если есть лучший подход, пожалуйста, дайте мне знать. Спасибо!
Ответ №1:
Я не уверен в лучшем способе сделать это «рельсовым способом», но чтобы просто сделать это в Postgresql, вы можете использовать оконные функции:
SELECT *, waist - LAG(waist) OVER (order by date) as change FROM fitness_measurements WHERE member_id = ?
Попробовал это на PostgreSQL 9.3.x, и это работает без проблем. Не пробовал это в Rails, но я думаю, вам нужно будет сделать что-то вроде:
sql = "SELECT *, waist - ..."
FitnessMeasurement.find_by_sql(sql)
...
Комментарии:
1. спасибо за ваш ответ. Я посмотрю на это и посмотрю, смогу ли я понять, как применить это к моему проекту Rails. Нужно выяснить, как изменить код моего метода запроса.
2. необработанный SQL вызывает следующую ошибку: синтаксическая ошибка, неожиданный ‘,’ sql = SELECT *, задержка талии (талии) (ord…
3. Вы действительно оставили кавычки? Была ли это ошибка ruby или ошибка postgres? Я должен попробовать запустить SQL в «rails dbconsole», чтобы сначала заставить его работать.
4. Я запустил SQL без кавычек. Я решил принять решение @David Alridge… это чище / лучше, чем мой первоначальный подход. Тем не менее, я ценю вашу помощь.
5. Я не могу прокомментировать ответ Дэвида Олдриджа (недостаточно репутации), но я бы посмотрел на добавление .limit(1) перед . в противном случае, похоже, что он вернет все предыдущие fitness_measurements из базы данных вместо одного.
Ответ №2:
Контроллер является неправильным местом для этого кода — для более простой реализации добавьте в модель метод, который содержит сохраненные значения.
def previous
self.class.where(member: self.member).
where("date < ?", self.date).
order(date: :desc).
take
end
Это дает вам предыдущий экземпляр для элемента, из которого вы затем можете считывать измерения, позволяя использовать такую логику, как:
@measurement.waist - @measurement.previous.waist
Комментарии:
1. Спасибо за это решение! Этот подход более чистый и намного лучше, чем у меня был. Это отлично работает. Потрясающе.