Я недавно развертывал некоторые приложения в Heroku. Я запускаю MySQL на своей локальной машине-разработчике и немного потратил некоторое время на обновление некоторых моих областей для работы в PostgreSQL. Однако одна из ошибок, которые я получил, трудно изменить.
В настоящее время у меня есть спецификация case в базе данных в моей модели. Я понимаю, почему возникает ошибка в отношении функций даты MySQL, но я не уверен, что это наиболее эффективное решение. У кого-нибудь есть лучший способ реализовать исправление, которое будет работать как с MySQL, так и с PostgreSQL?
case ActiveRecord::Base.connection.adapter_name
when 'PostgreSQL'
named_scope :by_year, lambda { |*args| {:conditions => ["published = ? AND (date_part('year', created_at) = ?)", true, (args.first)], :order => "created_at DESC"} }
named_scope :by_month, lambda { |*args| {:conditions => ["published = ? AND (date_part('month', created_at) = ?)", true, (args.first)], :order => "created_at DESC"} }
named_scope :by_day, lambda { |*args| {:conditions => ["published = ? AND (date_part('day', created_at) = ?)", true, (args.first)], :order => "created_at DESC"} }
else
named_scope :by_year, lambda { |*args| {:conditions => ["published = ? AND (YEAR(created_at) = ?)", true, (args.first)], :order => "created_at DESC"} }
named_scope :by_month, lambda { |*args| {:conditions => ["published = ? AND (MONTH(created_at) = ?)", true, (args.first)], :order => "created_at DESC"} }
named_scope :by_day, lambda { |*args| {:conditions => ["published = ? AND (DAY(created_at) = ?)", true, (args.first)], :order => "created_at DESC"} }
end
FYI, это ошибка PostgreSQL, которую я получаю:
PGError: ERROR: function month(timestamp without time zone) does not exist LINE 1: ...T * FROM "articles" WHERE (((published = 't' AND (MONTH(crea... ^ HINT: No function matches the given name and argument types. You might need to add explicit type casts. : SELECT * FROM "articles" WHERE (((published = 't' AND (MONTH(created_at) = '11')) AND (published = 't' AND (YEAR(created_at) = '2010'))) AND ("articles"."published" = 't')) ORDER BY created_at DESC LIMIT 5 OFFSET 0
Заранее благодарим за любой вклад.
Вы должны использовать стандартную функцию EXTRACT
:
named_scope :by_year, lambda { |*args| {:conditions => ["published = ? AND (extract(year from created_at) = ?)", true, (args.first)], :order => "created_at DESC"} }
Оба PostgresSQL и MySQL поддерживают его.
К сожалению, это случается много, однако у вас есть общая правильная идея.
Ваш первый метод атаки - посмотреть, есть ли функция, которая существует как в MySQL, так и в Postres, однако в этом случае это невозможно.
Одно из предложений, которое я бы сделал, это то, что в этом решении много дублирования кода. Рассмотрение утверждения условия является единственной совместимой проблемой здесь, я бы отмерил проверку совместимости только для условия:
Пример (Semi-Psuedo Code):
named_scope :by_year, lambda { |*args| {:conditions => ["published = ? AND (#{by_year_condition} = ?)", true, (args.first)], :order => "created_at DESC"} }
#...code...
def by_year_condition
if postgres
"date_part('year', created_at)"
else
"YEAR(created_at)"
end
Другим вариантом будет создание вычисленных столбцов для каждой из ваших частей даты (day
, month
и year
) и прямой запрос на них. Вы можете сохранить их в актуальном состоянии с помощью кода модели или с помощью триггеров. Вы также получите возможность индексировать различные комбинации на столбцах year
, month
и day
. Базы данных, как известно, плохо работают с использованием индексов при использовании функции в предложении where, особенно когда эта функция вытаскивает часть данных из середины столбца.
Потенциал наличия трех отдельных столбцов заключается в том, что ваш запрос больше не будет полагаться на какие-либо реализации SQL-производителей.