2013-06-18 8 views
13

में पिछले has_many रिकॉर्ड के आधार पर मैं एक Student मॉडल और एक Gpa मॉडल है। Student has_many Gpa है। मैं अपने हाल ही में बनाए गए जीपीए रिकॉर्ड के value विशेषता के आधार पर छात्रों को कैसे क्रमबद्ध करूं?क्रमबद्ध रेल

नोट: मैं एक व्यक्ति छात्र की बनाई गई तिथि के आधार पर GPAs सॉर्ट करने के लिए नहीं करना चाहती। मैं सभी छात्रों को खींचने और उन्हें सुलझाने के लिए चाहते हैं उनके सबसे हाल जीपीए रिकॉर्ड के आधार पर

class Student < ActiveRecord::Base 
    has_many :gpas 
end 

@students = Student.order(...) 
+1

आप कौन सी आरडीबीएमएस उपयोग कर रहे हैं? –

+0

मैं ActiveRecord –

+1

नहीं - PostgreSQL, Oracle, MySQL ... का उपयोग कर रहा हूं? –

उत्तर

10

GPAs टाइमस्टैम्प संभालने updated_at

है: http://guides.rubyonrails.org/association_basics.html

बस के लिए पृष्ठ पर एक कीवर्ड खोज करते हैं "has_and_belongs_to_many संघ इन विकल्पों का समर्थन करता है"

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 बनाता है पसंद नहीं है, तो आप कुछ कच्चे एसक्यूएल

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') 
0

शायद की तरह

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

यह काम नहीं करता है। मैं वही 'छात्र' के w/एकाधिक उदाहरणों को समाप्त करता हूं (क्योंकि तब एकाधिक जीपीए रिकॉर्ड होते हैं)। –

+0

DISTINCT –

4

कुछ मुझे यकीन है कि वहाँ किसी भी तरह से इस को प्राप्त करने का एक तरीका है कि नहीं कर रहा हूँ सुविधाजनक और अधिकतर रूबी तरीके से। एसक्यूएल एक कुशल कार्यान्वयन शायद के लिए आवश्यक में शामिल होने के आधार पर एक आदेश की आवश्यकता है - कुछ की तरह ...

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)") 

दूसरी तरफ, ऐसा लगता है कि अंतिम मूल्य एक महत्वपूर्ण होगा, इसलिए आप छात्रों को तालिका में "last_gpa_id" या "last_gpa_value" सेट करने के लिए gpas पर कॉलबैक लागू करना चाहेंगे अधिक कुशल में शामिल हों।

तो निश्चित रूप से कार्यान्वयन तुच्छ होगा।

+0

+1 जोड़ने के बारे में मुझे लगता है कि यह एक स्वीकार्य उत्तर होना चाहिए ... – j03w

+0

ठीक है, मुझे लगता है कि खंड द्वारा क्रमशः 'चयन' का उपयोग करना बहुत अनुकूल है। हालांकि, कैश 'last_gpa_id' या' last_gpa_value' मेरे लिए सबसे अच्छा समाधान प्रतीत होता है। – j03w

+0

@ j03w यह क्वेरी ऑप्टिमाइज़र पर निर्भर करता है। इसमें शामिल होने के रूप में एक सहसंबंधित सबक्वायरी को लागू करने के लिए सैद्धांतिक विकल्प है। निष्पादन योजना देखना दिलचस्प होगा, लेकिन मैं उम्मीद करता हूं कि यह बहुत अच्छा प्रदर्शन करे। –

0

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

फिर भी यह ध्यान रखें कि इस छात्र जो कोई gpas मिल गया है शामिल होंगे महत्वपूर्ण है। लेकिन आपको लगता है कि easly फ़िल्टर कर बाहर @students.delete_if{ |s| s.gpas.blank? }

+0

क्या इसमें सही शामिल है? क्या आप शामिल होने का मतलब है? –

+0

हां यह सही है। मेरा मतलब था। शामिल है। यह एक बाएं बाहरी जॉइन बनाता है जो .joins में एक ही आइटम के एकाधिक उदाहरण होने के नकारात्मक प्रभाव को अक्षम करता है। कोशिश करो! – p1100i

+0

ओह, दिलचस्प। और मैं परेशान हूं जिसमें अलग-अलग क्वेरी के रूप में उत्सुक लोडिंग को शामिल किया गया है, या अन्य परिस्थितियों में बाएं बाहरी शामिल हैं? –

0

साथ आप अपने मॉडल में रिश्ते के लिए कुछ विकल्पों को जोड़ने की कोशिश कर सकते कर सकते हैं।

कुछ की तरह:

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 

विकल्पों का उपयोग करना, तुम सब करने के लिए है एक फोन करना और रेल बड़े कार्य करने का सबसे करते हैं।

आप स्टंप्डया मिलता है, वहाँ कई और अधिक विकल्प यहां हैं:

-1

उपयोग कर सकते हैं पिछले जीपीए बनाया पाने के लिए एक last_gpa पद्धति बनाएं और फिर एक मानक प्रदर्शन रूबी प्रकार।

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


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

यह बेहद अक्षम है, यह सभी छात्रों को पुनर्प्राप्त करेगा, और फिर प्रत्येक जीपीए को पुनः प्राप्त करेगा। Imho आप डेटाबेस की शक्ति का लाभ उठाना चाहते हैं। – nathanvda

0

यह आपके लिए काम करना चाहिए। इस SQL ​​क्वेरी

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

यह छात्रों को आदेश नहीं देता है। रिकॉर्ड आईडी द्वारा आदेश दिया जाता है। –

0

मुझे लगता है कि कोशिश करो, आप इस विधि की कोशिश कर सकते:

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 
0

त्वरित और गंदे:

आप किसी भी तरह इस तरह एक प्रश्न लागू कर सकते हैं एआर वस्तुओं लाने के लिए आपको चाहिए:

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 

मान लीजिए कि आईडी उच्च निर्मित_ट वाले रिकॉर्ड के लिए अधिक है।

या

अच्छे तरीका:

मुझे लगता है, तो आप बहुत बार छात्र के अंतिम जीपीए अंक प्राप्त करने होंगे। छात्र मॉडल में नया कॉलम :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 फ़ील्ड के साथ जो भी चाहें कर सकते हैं।

0

आपको अपने डीबी से मेल खाने के लिए टेबल नाम और कॉलम नाम समायोजित करना होगा।

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 
संबंधित मुद्दे