Как я могу найти связанную таблицу, фильтрующую самые старые записи по одному из ее атрибутов?

0

Вопрос

У меня есть модель Subscription который has_many Versions.

A Version имеет status, plan_id и authorized_at Дата.

Любые изменения, внесенные в Subscription происходит от Version изменения, обновляющие его родительский Subscription.

Цель состоит в том, чтобы найти каждую подписку Version с самым старшим authorized_at Дата WHERE то versions.plan_id это то же самое, что и subscriptions.plan_id (другими словами, мне нужна дата авторизации Version Где plan_id изменен на текущий Subscriptionplan_id).

Это вопрос, который я задал. Я получаю ошибку в синтаксисе агрегатной функции:

syntax error at or near "MIN" LINE 3: MIN (authorized_at) from versions ^

запрос:

select subscriptions.id,
MIN (authorized_at) from versions
where versions.plan_id = subscriptions.plan_id
) as current_version
from subscriptions
join versions on subscriptions.id = versions.subscription_id
where versions.status = 'processed'

Я также не уверен, следует ли мне группировать версии по plan_id а затем выбираем из каждой группы. Я вроде как заблудился.

database postgresql ruby ruby-on-rails
2021-11-23 14:26:06
3

Лучший ответ

1

Вы можете использовать боковой подзапрос, который лучше всего можно описать как цикл foreach в SQL. Они являются чрезвычайно эффективным способом выбора столбцов из одной коррелированной записи или даже агрегатов из группы связанных записей.

Для каждой строки в подписках база данных выберет одну строку из версий, заказанных authorized_at:

SELECT "subscriptions".*,
       "latest_version"."authorized_at" AS current_version,
       "latest_version"."id" AS current_version_id -- could be very useful
FROM   "subscriptions" 
LATERAL
  (
     SELECT   "versions"."authorized_at", "versions"."id"
     FROM     "versions"
     WHERE    "versions"."subscription_id" = "subscriptions"."id" -- lateral reference
     AND      "versions"."plan_id" = "subscriptions"."plan_id"
     AND      "versions"."status" = 'processed'
     ORDER BY "versions"."authorized_at" ASC
     LIMIT 1
  ) latest_version ON TRUE

Создание боковых соединений в ActiveRecord может быть выполнено либо с помощью строк SQL, либо с помощью Arel:

class Subscription < ApplicationRecord
  # Performs a lateral join and selects the 
  # authorized_at of the latest version 
  def self.with_current_version
    lateral = Version.arel_table.then do |v|
      v.project(
        v[:authorized_at],
        v[:id] # optional
      ).where(
        v[:subscription_id].eq(arel_table[:id])
          .and(v[:plan_id].eq(arel_table[:plan_id]) )
          .and(v[:status].eq('processed'))
      )
      .order(v[:authorized_at].asc)
      .take(1) # limit 1
      .lateral('latest_version ON TRUE')
    end
    lv = Arel::Table.new(:latest_version) # just a table alias
    select(
      *where(nil).arel.projections, # selects everything previously selected
      lv[:authorized_at].as("current_version"),
      lv[:id].as("current_version_id") # optional
    ).joins(lateral.to_sql)
  end
end

Если вы просто хотите выбрать id и current_version столбец вам следует рассмотреть возможность использования pluck вместо выбора моделей баз данных, которые недостаточно увлажнены.

2021-11-23 16:49:48
1

Вы можете использовать DISTINCT ON чтобы отфильтровать строки и сохранить одну строку на подписку-первую на группу в соответствии с ORDER BY пункт.

Например:

select distinct on (s.id) s.id, v.authorized_at
from subscription s
join versions v on v.subscription_id = s.id and v.plan_id = s.plan_id
where v.status = 'processed'
order by s.id, v.authorized_at
2021-11-23 16:40:51

Спасибо. Запрос, похоже, работает в консоли, но когда я пытаюсь использовать его в своих представлениях в качестве области модели, я получаю ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR: syntax error at or near "WHERE" LINE 6: ...s.id, v.authorized_at WHERE "sub...
pinkfloyd90

@pinkfloyd90 Я не эксперт по ActiveRecord, но, возможно, это не работает, потому что представление, вероятно, невозможно обновить.
The Impaler
0

Приведенный ниже код даст вам versions где версия plan_id равно стоимости подписки plan_id.

@versions = Version.joins("LEFT JOIN subscriptions ON subscriptions.plan_id = versions.plan_id")

Для фильтрации записей по версиям status

@versions = Version.joins("LEFT JOIN subscriptions ON subscriptions.plan_id = versions.plan_id").where(status: "processed")

Для фильтрации записей по версиям status и заказать по authorized_at в порядке возрастания.

@versions = Version.joins("LEFT JOIN subscriptions ON subscriptions.plan_id = versions.plan_id").where(status: "processed").order(:authorized_at)

Для фильтрации записей по версиям status и заказать по authorized_at в порядке убывания.

@versions = Version.joins("LEFT JOIN subscriptions ON subscriptions.plan_id = versions.plan_id").where(status: "processed").order(authorized_at: :desc)

Надеюсь, это сработает для вас!

2021-11-23 14:33:48

Я думаю, тебе нужно присоединиться subscriptions.id = versions.subscription_id или в результате вы получите версии, которые не относятся к этой подписке. Вы также даже не затронули суть вопроса, который заключается в выборе совокупности.
max

На других языках

Эта страница на других языках

Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................