Подтвердить что ты не робот

Сортировка по последней записи has_many в Rails

У меня есть модель Student и модель Gpa. Student has_many Gpa. Как я должен сортировать учеников на основе их недавно созданного атрибута gpa record value?

ПРИМЕЧАНИЕ. Я не хочу сортировать отдельные студенческие GPA на основе созданной даты. Я хотел бы вытащить ВСЕ студентов и отсортировать их по их последней записи GPA.

class Student < ActiveRecord::Base
  has_many :gpas
end

@students = Student.order(...)
4b9b3361

Ответ 1

Предполагая, что метка времени gpas updated_at

Student.joins(:gpas).order('gpas.updated_at DESC').uniq

Чтобы включить студентов без gpas

#references is rails 4; works in rails 3 without it
Student.includes(:gpas).order('gpas.updated_at DESC').references(:gpas).uniq

если вам не нравится distinct, созданный uniq, вы можете использовать некоторый raw sql

Student.find_by_sql("SELECT students.* FROM students
INNER JOIN gpas ON gpas.student_id = students.id
LEFT OUTER JOIN gpas AS future ON future.student_id = gpas.student_id
  AND future.updated_at > gpas.updated_at
WHERE future.id IS NULL ORDER BY gpas.updated_at DESC")
# or some pretty raw arel
gpa_table = Gpa.arel_table
on = Arel::Nodes::On.new(
  Arel::Nodes::Equality.new(gpa_table[:student_id], Student.arel_table[:id])
)
inner_join = Arel::Nodes::InnerJoin.new(gpa_table, on)
future_gpa_table = Gpa.arel_table.alias("future")
on = Arel::Nodes::On.new(
  Arel::Nodes::Equality.new(future_gpa_table[:student_id], gpa_table[:student_id]).\
  and(future_gpa_table[:updated_at].gt(gpa_table[:updated_at])
  )
)
outer_join = Arel::Nodes::OuterJoin.new(future_gpa_table, on)
# get results
Student.joins(inner_join).joins(outer_join).where("future.id IS NULL").\
order('gpas.updated_at DESC')

Ответ 2

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

select
  ...
from
  students
order by 
  (  select gpas.value
       from gpas
      where gpas.student_id = student.id
   order by gpas.as_of_date desc
      limit 1)

Я не уверен, что это законно в MySQL, но если это возможно, возможно, просто:

Student.order("(select gpas.value from gpas where gpas.student_id = student.id order by gpas.as_of_date desc limit 1)")

С другой стороны, кажется, что последнее значение будет важным, поэтому вам может потребоваться выполнить обратный вызов gpas для установки "last_gpa_id" или "last_gpa_value" в таблице студентов, чтобы сделать это общее соединение более эффективным.

Тогда, конечно, реализация была бы тривиальной.

Ответ 3

Возможно, что-то вроде

Student.joins(:gpas).order("gpas.value DESC")

Ответ 4

@students = Student.includes(:gpas).order('gpas.value DESC')

Тем не менее важно отметить, что это будут студенты, у которых нет gpas. Но вы можете легко фильтровать его с помощью @students.delete_if{ |s| s.gpas.blank? }

Ответ 5

Вы можете попробовать добавить некоторые параметры в отношения в своих моделях.

Что-то вроде:

class Student < ActiveRecord::Base
  has_many :gpas, order: "value DESC", conditions: "foo = bar" #<-whatever conditions you want here
end

class Gpa < ActiveRecord::Base
  belongs_to :student 
end

Используя опции, все, что вам нужно сделать, сделать звонок и позволить Rails делать большую часть тяжелого подъема.

Если вы попадаете в тупик, здесь есть еще несколько вариантов: http://guides.rubyonrails.org/association_basics.html

Просто выполните поиск по ключевым словам на странице "Ассоциация has_and_belongs_to_many поддерживает следующие параметры:"

Ответ 6

Это должно сработать для вас. Попробуйте этот SQL-запрос

SELECT * FROM students WHERE id IN 
        (SELECT student_id 
            FROM gpa 
            GROUP BY student_id 
            ORDER BY created_at DESC);

Ответ 7

Я думаю, вы могли бы попробовать этот метод:

class Student < ActiveRecord::Base
  attr_accessible :name
  has_many :gpas

  def self.by_last_gpas
    sql = <<-SQL
      select students.*,
        (
          select gpas.created_at from gpas where student_id=students.id
          order by gpas.created_at desc
          limit 1
        ) as last_created_at
      from students
      order by last_created_at desc
    SQL
    Student.find_by_sql(sql)
  end
end

Ответ 8

Быстрая и грязная:

Вы можете каким-то образом реализовать такой запрос, чтобы получить требуемые объекты AR:

select s.* from students s, gpas g 
  where s.id = gpas.student_id 
  and gpas.id in (select max(id) from gpas group by student_id)
  order by gpas.value

Предполагая, что идентификатор выше для записей с более высоким значением created_at.

ИЛИ

Более чистый способ:

Я предполагаю, что вам понадобится последний показатель GPA у студентов. Почему бы не добавить новый столбец :last_gpa_score к модели ученика?

Вы можете использовать обратный вызов, чтобы сохранить это поле согласованным и автозаполненным, i. е:.

class Gpa < ActiveRecord::Base
  belongs_to :student
  after_save :update_student_last_score

  private
    def update_student_last_score
      self.student.update_last_gpa_score
    end
end

class Student < ActiveRecord::Base
  has_many :gpas

  def update_last_gpa_score
    self.last_gpa_score = self.gpas.order("created_at DESC").first.value
  end
end

Затем вы можете делать все, что хотите, с помощью поля last_gpa_score на ученике.

Ответ 9

Вам нужно будет настроить имена таблиц и имена столбцов в соответствии с вашей БД.

SELECT s.*, g.*
  FROM (SELECT studentId, MAX(gpaDate) as gpaDate FROM gpas GROUP BY studentId) maxgpa
  JOIN gpas g
    ON g.studentid = maxgpa.studentId
   AND g.gpaDate   = maxgpa.gpaDate
  JOIN students s
    ON s.studentId = g.studentId
 ORDER g.gpaDate DESC

Ответ 10

Создайте метод last_gpa, чтобы создать последний GPA, а затем выполните стандартную сортировку Ruby.

def last_gpa
  a.gpas.order(:order=>"created_at DESC").first
end


Student.all.sort { |a, b| a.last_gpa <=> b.last_gpa }