ActiveRecord หรือแบบสอบถาม


181

คุณจะทำการสืบค้น OR ใน Rails 3 ActiveRecord ได้อย่างไร ตัวอย่างทั้งหมดที่ฉันพบมีเพียงแบบสอบถาม

แก้ไข: หรือวิธีนี้มีให้ตั้งแต่ Rails 5. ดูActiveRecord :: QueryMethods


7
เรียนรู้ SQL ActiveRecord ทำให้การทำงานของคุณเป็นเรื่องง่าย แต่คุณยังจำเป็นต้องรู้ว่าข้อความค้นหาของคุณทำงานเป็นอย่างไรมิฉะนั้นโครงการของคุณอาจไม่ขยาย ฉันก็คิดว่าฉันสามารถผ่านมาได้โดยไม่ต้องจัดการกับ SQL และฉันก็ผิดมาก
— ryan0

1
@ ryan0 คุณพูดถูก เราอาจใช้อัญมณีแฟนซีและสิ่งอื่น ๆ แต่เราควรระวังสิ่งที่อัญมณีเหล่านี้กำลังทำอยู่ข้างในและเทคโนโลยีพื้นฐานคืออะไรและอาจใช้เทคโนโลยีพื้นฐานโดยปราศจากความช่วยเหลือของอัญมณีหากจำเป็นอาจเกิดขึ้นเพื่อประโยชน์ของ ประสิทธิภาพ. อัญมณีถูกสร้างขึ้นด้วยจำนวน usecases ที่เฉพาะเจาะจง แต่อาจมีสถานการณ์ที่ usecase หนึ่งในโครงการของเราอาจแตกต่างกัน
— rubyprince


1
ตั้งแต่ Rails 5 IMHO คำตอบที่ยอมรับควรเป็นรุ่น Greg Olsen:Post.where(column: 'something').or(Post.where(other: 'else'))
— Philipp Claßen

6
คำถามนี้มีแท็กของ ruby-on-rails-3 ทำไมคำตอบที่ได้รับการยอมรับจึงเกี่ยวข้องกับ Rails 5 เท่านั้น
— iNulty

คำตอบ:


109

ใช้ARel

t = Post.arel_table

results = Post.where(
  t[:author].eq("Someone").
  or(t[:title].matches("%something%"))
)

ผลลัพธ์ SQL:

ree-1.8.7-2010.02 > puts Post.where(t[:author].eq("Someone").or(t[:title].matches("%something%"))).to_sql
SELECT     "posts".* FROM       "posts"  WHERE     (("posts"."author" = 'Someone' OR "posts"."title" LIKE '%something%'))

รู้สึกยุ่งเล็กน้อย แต่อย่างน้อยฉันไม่ได้เขียน sql ซึ่งรู้สึกผิด! ฉันจะต้องใช้ Arel ให้มากขึ้น
— pho3nixf1re

55
ฉันไม่สามารถเชื่อได้ว่าภาคต่อที่งดงามยิ่งกว่านั้นคืออะไร
— Macario

3
ไม่มีอะไรผิดปกติกับ SQL สิ่งที่สามารถไปผิดคือวิธีที่เราสร้างสตริง SQL ได้แก่SQL Injection ดังนั้นเราจะต้องฆ่าเชื้ออินพุตของผู้ใช้ก่อนที่จะให้คิวรี SQL ที่ต้องรันกับฐานข้อมูล สิ่งนี้จะได้รับการจัดการโดยORM s และสิ่งเหล่านี้ได้รับการจัดการกรณีขอบที่เรามักจะพลาด ดังนั้นจึงแนะนำให้ใช้ORMเพื่อสร้างแบบสอบถาม SQL
— rubyprince

5
ปัญหาอีกอย่างหนึ่งของ SQL ก็คือมันไม่ใช่ฐานข้อมูลไม่เชื่อเรื่องพระเจ้า ตัวอย่างเช่นmatchesจะสร้างLIKEสำหรับ sqlite (case insensitive โดยค่าเริ่มต้น) และILIKEpostgres (ต้องการ case insensitive ที่ชัดเจนเช่นโอเปอเรเตอร์)
— อะมีบา

1
@ToniLeigh ActiveRecord กำลังใช้งาน SQL ภายใต้ประทุน มันเป็นเพียงไวยากรณ์สำหรับการเขียนเคียวรี SQL ที่จัดการกับการฆ่าเชื้อ SQL และทำให้เคียวรี DB ของคุณไม่เชื่อเรื่องพระเจ้า โอเวอร์เฮดเดียวคือที่ Rails แปลงไวยากรณ์เป็นเคียวรี SQL และมันก็เป็นเพียงเรื่องของมิลลิวินาที Ofcourse คุณควรเขียนแบบสอบถาม SQL performant ที่ดีที่สุดและพยายามแปลงเป็นไวยากรณ์ ActiveRecord หากไม่สามารถแสดง SQL ในไวยากรณ์ ActiveRecord คุณควรไปที่ SQL ธรรมดา
— rubyprince

208

หากคุณต้องการใช้ตัวดำเนินการ OR กับค่าของคอลัมน์หนึ่งคุณสามารถส่งอาร์เรย์ไปยัง.whereและ ActiveRecord จะใช้IN(value,other_value):

Model.where(:column => ["value", "other_value"]

เอาท์พุท:

SELECT `table_name`.* FROM `table_name` WHERE `table_name`.`column` IN ('value', 'other_value')

สิ่งนี้ควรบรรลุความเท่าเทียมกันของORหนึ่งคอลัมน์


13
นี้เป็นที่สะอาดที่สุด "รางทาง" คำตอบที่ควรได้รับการยอมรับ
— koonse

10
ขอบคุณ @koonse ก็ไม่ได้ว่าการสอบถาม 'หรือ' แต่มันก่อผลเดียวกันสำหรับสถานการณ์นี้
— deadkarma

ช่วยถ้าคุณทำได้ - stackoverflow.com/questions/15517286/…
— Pratik Bothra

80
วิธีนี้ไม่ใช่การแทนที่หรือเพราะคุณไม่สามารถใช้สองคอลัมน์ที่แตกต่างกันในลักษณะนี้
— fotanus

152

ใน Rails 3 ควรเป็น

Model.where("column = ? or other_column = ?", value, other_value)

นอกจากนี้ยังรวมถึง sql ดิบ แต่ฉันไม่คิดว่ามีวิธีใน ActiveRecord ที่จะทำหรือดำเนินการ คำถามของคุณไม่ใช่คำถาม noob


41
แม้ว่าจะมี sql raw - คำสั่งนี้ดูชัดเจนสำหรับฉันมากกว่า Arel แม้กระทั่ง DRYer
— jibiel

6
หากคุณต้องการเชื่อมโยงตารางอื่น ๆ ที่เข้าร่วมซึ่งอาจมีคอลัมน์ที่มีชื่อคอลัมน์เดียวกันคุณควรใช้มันเช่นนี้Page.where("pages.column = ? or pages.other_column = ?", value, other_value)
— rubyprince

1
และที่ล้มเหลวหากแบบสอบถามนามแฝงตาราง นั่นคือเมื่อ arel ส่องแสง
— DGM

58

Rails / ActiveRecord รุ่นที่ปรับปรุงแล้วอาจรองรับไวยากรณ์นี้ได้ มันจะมีลักษณะคล้ายกับ:

Foo.where(foo: 'bar').or.where(bar: 'bar')

ตามที่ระบุไว้ในคำขอดึงนี้https://github.com/rails/rails/pull/9052

สำหรับตอนนี้เพียงแค่ติดกับงานต่อไปนี้ได้ดี:

Foo.where('foo= ? OR bar= ?', 'bar', 'bar')

ปรับปรุง:ตามhttps://github.com/rails/rails/pull/16052orคุณลักษณะจะสามารถใช้ได้ใน Rails 5

อัพเดท:ฟีเจอร์ถูกรวมเข้ากับ Rails 5 branch


2
orใช้งานได้แล้วตอนนี้ Rails 5 แต่ไม่สามารถใช้งานได้เนื่องจากคาดว่าจะมีการส่งอาร์กิวเมนต์ 1 รายการ มันคาดว่าวัตถุ Arel ดูคำตอบที่ยอมรับ
— El'Magnifico

2
orวิธีการใน ActiveRecord ทำงานหากคุณกำลังใช้โดยตรง แต่สามารถทำลายความคาดหวังของคุณถ้ามันใช้อยู่ในขอบเขตซึ่งเป็นที่ถูกล่ามโซ่แล้ว
— รายการเจเรมี

@JeremyList พูดว่าอะไรเป็นจุดสนใจ นั่นทำให้ฉันลำบาก
— courtimas

36

Rails ได้เพิ่มสิ่งนี้ลงใน ActiveRecord ดูเหมือนว่าจะเปิดตัวใน Rails 5 มุ่งมั่นที่จะโทแล้ว:

https://github.com/rails/rails/commit/9e42cf019f2417473e7dcbfcb885709fa2709f89

Post.where(column: 'something').or(Post.where(other: 'else'))

# => SELECT * FROM posts WHERE (column = 'something') OR (other = 'else)


14

หากคุณต้องการใช้อาร์เรย์เป็นอาร์กิวเมนต์รหัสต่อไปนี้จะทำงานใน Rails 4:

query = Order.where(uuid: uuids, id: ids)
Order.where(query.where_values.map(&:to_sql).join(" OR "))
#=> Order Load (0.7ms)  SELECT "orders".* FROM "orders" WHERE ("orders"."uuid" IN ('5459eed8350e1b472bfee48375034103', '21313213jkads', '43ujrefdk2384us') OR "orders"."id" IN (2, 3, 4))

ข้อมูลเพิ่มเติม: หรือแบบสอบถามกับอาร์เรย์เป็นข้อโต้แย้งในทางรถไฟ 4


9
หรือนี่คือOrder.where(query.where_values.inject(:or))การใช้ arel ตลอดทาง
— Cameron Martin

> หรือแบบสอบถามที่มีอาร์เรย์เป็นอาร์กิวเมนต์ใน Rails 4. ลิงค์ที่มีประโยชน์! ขอบคุณ!
— Zeke Fast

7

MetaWhereปลั๊กอินเป็นที่น่าตื่นตาตื่นใจอย่างสมบูรณ์

ผสม OR หรือ AND และร่วมกับเงื่อนไขในการเชื่อมโยงใด ๆ ได้อย่างง่ายดายและยังระบุ OUTER JOIN ของ!

Post.where({sharing_level: Post::Sharing[:everyone]} | ({sharing_level: Post::Sharing[:friends]} & {user: {followers: current_user} }).joins(:user.outer => :followers.outer}

6

เพียงเพิ่มหรือในเงื่อนไข

Model.find(:all, :conditions => ["column = ? OR other_column = ?",value, other_value])

4
นี่เป็นไวยากรณ์สำหรับ Rails 2 มากกว่าและฉันต้องเขียนอย่างน้อยส่วนของสตริง sql
— pho3nixf1re

3

ฉันเพิ่งแยกปลั๊กอินนี้จากงานลูกค้าที่ให้คุณรวมขอบเขตกับ.or., ex. Post.published.or.authored_by(current_user). Squeel (การใช้ MetaSearch รุ่นใหม่กว่า) นั้นยอดเยี่ยมเช่นกัน แต่ไม่อนุญาตให้คุณหรือขอบเขตดังนั้นตรรกะการสืบค้นจึงสามารถซ้ำซ้อนได้เล็กน้อย


3

ด้วย Rails + arel วิธีที่ชัดเจนยิ่งขึ้น:

# Table name: messages
#
# sender_id:    integer
# recipient_id: integer
# content:      text

class Message < ActiveRecord::Base
  scope :by_participant, ->(user_id) do
    left  = arel_table[:sender_id].eq(user_id)
    right = arel_table[:recipient_id].eq(user_id)

    where(Arel::Nodes::Or.new(left, right))
  end
end

ผลิต:

$ Message.by_participant(User.first.id).to_sql 
=> SELECT `messages`.* 
     FROM `messages` 
    WHERE `messages`.`sender_id` = 1 
       OR `messages`.`recipient_id` = 1

3

คุณสามารถทำได้เช่น:

Person.where("name = ? OR age = ?", 'Pearl', 24)

หรือมากกว่านั้นให้ติดตั้งrails_or gem และทำเช่น:

Person.where(:name => 'Pearl').or(:age => 24)

-2

ฉันต้องการเพิ่มสิ่งนี้เป็นวิธีแก้ปัญหาเพื่อค้นหาคุณลักษณะหลายอย่างของ ActiveRecord ตั้งแต่

.where(A: param[:A], B: param[:B])

จะค้นหา A และ B


-4
Book.where.any_of(Book.where(:author => 'Poe'), Book.where(:author => 'Hemingway')

2
เพิ่มคำอธิบายให้กับคำตอบของคุณ
— kvorobiev

1
ฉันเชื่อว่ามัทธิวพูดถึงอัญมณีactiverecord_any_ofซึ่งเพิ่มการสนับสนุนสำหรับไวยากรณ์นี้
— DannyB

2
หากเป็นจริงขอขอบคุณ @DannyB คำตอบต้องอธิบายบริบทของมัน
— Sebastialonso
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.