У меня есть запрос ActiveRecord:
Post.all.select { |p| Date.today < p.created_at.weeks_since(2) }
И я хочу иметь возможность видеть, какой SQL-запрос это производит, используя .to_sql
Я получаю сообщение об ошибке: NoMethodError: undefined method 'to_sql'
ТИА!
Вам нужно вызвать to_sql
на отношения. select
выполняет запрос и дает вам результат, а в результате у вас нет to_sql
метода.
Есть похожие вопросы, на которые вы можете посмотреть, так как они предлагают некоторые альтернативы.
«выбрать выполняет запрос» - не обязательно. Это смотря какая select
это, из Enumerable
или активной записи. Post.select(:created_at)
не выполняет запрос, например (и вы можете вызвать .to_sql
на этом)
ПРОБЛЕМА
Есть 2 типа select
, когда дело доходит до ActiveRecord
объектов, из Документов
select
с блоком.Во-первых: берет блок, чтобы его можно было использовать так же, как
Array#select
.
Это создаст массив объектов из базы данных для области видимости, преобразуя их в массив и перебирая их с помощьюArray#select
.
Это то, что вы используете прямо сейчас. Эта реализация будет загружать каждое сообщение, создавая экземпляр объекта Post
, а затем перебирать каждый Post
, используя Array#select
, чтобы отфильтровать результаты в Array
. Это крайне неэффективно, не может быть связано с другой семантикой AR (например, where
, order
и т. д.) и приведет к очень длительным задержкам в масштабе. (Это также является причиной вашей ошибки, потому что у Array
нет метода to_sql
)
select
со списком столбцов (или строкой, если хотите)Во-вторых: изменяет оператор SELECT для запроса, чтобы извлекались только определенные поля...
Эта версия не нужна в вашем случае, поскольку вы не хотите ограничивать столбцы, возвращаемые запросом, сообщениями.
Предлагаемое решение:
Вместо этого вы ищете предложение WHERE для фильтрации записей на уровне базы данных, прежде чем возвращать их в ORM.
Ваш текущий фильтр (X < Y + 2)
Date.today < p.created_at.weeks_since(2)
что означает, что сегодняшняя дата меньше, чем Дата создания плюс 2 недели.
Мы можем инвертировать этот критерий, чтобы упростить запрос, переключив его на «Сегодняшняя дата минус 2 недели» меньше, чем «Создано в». (Х - 2 < Y)
Date.today.weeks_ago(2) < p.created_at
Это эквивалентно p.created_at > Date.today.weeks_ago(2)
, который мы можем преобразовать в предложение where, используя стандартные методы запроса ActiveRecord:
Post.where(created_at: Date.today.weeks_ago(2)...)
Это приведет к SQL, например:
SELECT
posts.*
FROM
posts.*
WHERE
posts.created_at > '2022-10-28'
Заметки:
created_at
— это метка времени, поэтому лучше использовать Time.now
вместо Date.today
.
select
в этом выражении содержится рубиновый блок, поэтому его невозможно представить в SQL (поскольку эта часть не работает в базе данных)