استعلام القضبان المعقدة - النقابات؟اختيار فرعي؟هل لا يزال بإمكاني استخدام name_scope؟
-
06-07-2019 - |
سؤال
جزء من سبب حبي لـ Rails هو أنني أكره SQL - أعتقد أنها أشبه بلغة تجميع يجب معالجتها بأدوات ذات مستوى أعلى مثل ActiveRecord.يبدو أنني قد وصلت إلى حدود هذا النهج، وأنا خارج نطاق فهمي مع SQL.
لدي نموذج معقد يحتوي على الكثير من السجلات الفرعية.لدي أيضًا مجموعة من 30 إلى 40 نطاقًا مسمىًا تنفذ منطق العمل من العميل.يتم ربط هذه النطاقات معًا بشكل مشروط، ولهذا السبب لدي تلك النطاقات joins_
النطاقات حتى لا تتعرض الروابط للضرب.
لدي اثنين منها لا يعملان بشكل صحيح، أو على الأقل لا يعملان بالشكل الذي يريده العميل.فيما يلي فكرة تقريبية عن بنية النموذج، مع بعض النطاقات المسماة (ليست كلها مطلوبة للمثال) التي توضح نهجي وتشير إلى مشاكلي. (يرجى أن يغفر أي أخطاء في بناء الجملة)
class Man < ActiveRecord::Base
has_many :wives
named_scope :has_wife_named lambda { |n| { :conditions => { :wives => {:name => n}}}}
named_scope :has_young_wife_named lambda { |n| { :conditions => { :wives => {:name => n, :age => 0..30}}}}
named_scope :has_yw_named_v2 lambda { |n| { :conditions => ["wives.name = ? AND wives.age <= 30", n]}}
named_scope :joins_wives :joins => :wives
named_scope :has_red_cat :conditions => { :cats => {:color => 'red'}}
named_scope :has_cat_of_color lambda { |c| { :conditions => { :cats => {:color => c}}}}
named_scope :has_7yo_cat :conditions => { :cats => {:age => 7}}
named_scope :has_cat_of_age lambda { |a| { :conditions => { :cats => {:age => a}}}}
named_scope :has_cat_older_than lambda { |a| { :conditions => ["cats.age > ?", a] }}
named_scope :has_cat_younger_than lambda { |a| { :conditions => ["cats.age < ?", a] }}
named_scope :has_cat_fatter_than lambda { |w| { :conditions => ["cats.weight > ?", w] } }
named_scope :joins_wives_cats :joins => {:wives => :cats}
end
class Wife < ActiveRecord::Base
belongs_to :man
has_many :cats
end
class Cat < ActiveRecord::Base
belongs_to :wife
end
يمكنني العثور على رجال لديهم قطط زوجاتهم حمراء وعمرها سبع سنوات
@men = Man.has_red_cat.has_7yo_cat.joins_wives_cats.scoped({:select => 'DISTINCT men'})
ويمكنني أيضًا العثور على رجال لديهم قطط يزيد وزن زوجاتهم عن 20 رطلاً ويزيد عمرها عن 6 سنوات
@men = Man.has_cat_fatter_than(20).has_cat_older_than(5).joins_wives_cats.scoped({:select => 'DISTINCT men'})
ولكن هذا ليس ما أريد.أريد العثور على الرجال الذين يوجد بين زوجاتهم قطة حمراء واحدة على الأقل وقطة واحدة تبلغ من العمر سبع سنوات، والتي لا يلزم أن تكون نفس القطة، أو العثور على الرجال الذين يوجد بين زوجاتهم قطة واحدة على الأقل فوق وزن معين و قطة واحدة أكبر من عمر معين.
(في الأمثلة اللاحقة، يرجى افتراض وجود المناسبjoins_
وDISTINCT
)أستطيع أن أجد رجالاً لديهم زوجات تدعى إستير
@men = Man.has_wife_named('Esther')
يمكنني حتى أن أجد رجالًا لديهم زوجات تدعى إستير أو روث أو آدا (لطيفة!)
@men = Man.has_wife_named(['Esther', 'Ruth', 'Ada'])
لكني أريد أن أجد رجالًا لديهم زوجات يُسمون إستير وراعوث وآدا.
هههه فقط أمزح، في الواقع، أريد هذا:يمكنني العثور على رجال لديهم زوجات أقل من 30 عامًا يُدعى إستير
@men = Man.has_young_wife_named('Esther')
ابحث عن رجال لديهم زوجات شابات يُدعى إستير أو روث أو آدا
@men = Man.has_young_wife_named(['Esther', 'Ruth', 'Ada'])
ولكن كما هو مذكور أعلاه، أريد العثور على رجال لديهم زوجات شابات يُدعى إستير وراعوث وآدا.ولحسن الحظ، الحد الأدنى ثابت في هذه الحالة، ولكن سيكون من الجيد تحديد حد أدنى للعمر أيضًا.
هل هناك طريقة لاختبار عدم المساواة باستخدام بناء جملة التجزئة، أم أنه يتعين عليك دائمًا الرجوع إليها
:conditions => ["", n]
- لاحظ الفرق بينhas_young_wife_named
وhas_yw_named_v2
- أحب الأول أكثر، لكن النطاق يعمل فقط مع القيم المحدودة.إذا كنت تبحث عن زوجة عجوز، أعتقد أنه يمكنك الاستفادة منهاa..100
ولكن بعد ذلك عندما تبلغ الزوجة 101 عامًا، تتوقف عن البحث. (همم.هل هي تستطيع الطبخ؟ي / ك)هل هناك طريقة لاستخدام نطاق داخل النطاق؟أنا أحب ذلك إذا
:has_red_cat
يمكن استخدامها:has_cat_of_color
بطريقة أو بأخرى، أو إذا كانت هناك طريقة ما لاستخدام النطاق من سجل فرعي في سجله الأصلي، حتى أتمكن من وضع النطاقات ذات الصلة بالقطط فيWife
نموذج.
لا أريد حقًا القيام بذلك في لغة SQL مباشرة دون استخدام named_scope
, ، ما لم يكن هناك شيء آخر أفضل بالفعل - اقتراحات للمكونات الإضافية وما لا يحظى بتقدير كبير، أو التوجيه إلى نوع SQL الذي سأحتاج إلى تعلمه.اقترح أحد الأصدقاء أن عمليات البحث الفرعية أو UNIONs ستعمل هنا، ولكن لا يبدو أنها تمت مناقشتها كثيرًا في سياق Rails.لا أعرف حتى الآن أي شيء عن طرق العرض - هل ستكون مفيدة؟هل هناك طريقة سعيدة لصنع القضبان؟
شكرًا لك!
كما كنت ذاهبا إلى سانت آيفز
التقيت برجل لديه سبع زوجات
وكان لكل زوجة سبعة أكياس
كان في كل كيس سبع قطط
كان لكل قطة سبع مجموعات
أطقم، قطط، أكياس، زوجات
كم عدد الذين كانوا ذاهبين إلى سانت آيفز؟
المحلول
حسنًا، لقد حصلت على نتائج رائعة مع named_scope
مثل هذه:
named_scope :has_cat_older_than lambda { |a| { :conditions => ["men.id in ( select man_id from wives where wives.id in ( select wife_id from cats where age > ? ) )", a] } }
و
named_scope :has_young_wife_named lambda { |n| { :conditions => ["men.id in ( select man_id from wives where name = ? and age < 30)", n] } }
أستطيع الآن أن أفعل ذلك بنجاح
Member.has_cat_older_than(6).has_young_wife_named('Miriam').has_young_wife_named('Vashti')
والحصول على ما أتوقع.لا تتطلب هذه النطاقات استخدام الصلات، ويبدو أنها تعمل بشكل جيد مع الصلات ذات الأنماط الأخرى.
w00t!
تم التعليق على ما إذا كانت هذه طريقة فعالة للقيام بذلك، أو ما إذا كانت هناك طريقة أكثر "سككًا".قد تكون طريقة ما لتضمين نطاق من نموذج آخر كجزء من استعلام فرعي SQL مفيدًا ...
نصائح أخرى
لقد استخدمت build_finder_sql لإنجاز التحديد الفرعي لنطاق مسمى داخل نطاق آخر.قد لا يكون مناسبًا للجميع، ولكن استخدامه يسمح لنا بتجفيف بعض النطاقات المسماة التي استخدمناها للتقارير.
Man.has_cat_older_than(6).send(:construct_finder_sql,{})
جرب ذلك في البرنامج النصي/وحدة التحكم الخاصة بك.
لقد استخدمت الحل الأكثر أصالة لـ Rails.سيكون لـ Straight SQL نفس الأداء لذا لا يوجد سبب لاستخدامه.