我是Ruby on Rails和ActiveRecord的新手。我有一个名为location
的数据库模型,它描述了地图上的兴趣点。一个位置有一个location_type
字段,可以有三种不同的位置类型(业务,药房或联系人)。一个位置也有一个owner_id
,它是创建该位置的用户的user_id
。
在控制器中,用户通过提供他们的ID来请求他们的所有位置。药房和营业地点是公共的,因此所有用户都应该能够查看它们,而联系人只应向作为其所有者的用户显示。因此,我的任务是创建一个ActiveRecord查询,该查询返回数据库中的所有药房和企业以及该用户创建的所有联系人。我试图通过将条款链接在一起来做到这一点,但由于某种原因,这已经失败了:
@locations = Location.where(:location_type => ["business", "dispensary"]).where(:location_type => "contact", :owner_id => params[:id])
生成这个PostgreSQL:
SELECT "locations".* FROM "locations" WHERE "locations"."location_type" IN ('business', 'dispensary') AND "locations"."location_type" = 'contact' AND "locations"."owner_id" = 1
我怀疑这是失败的,因为第一个where
只返回商业和药房类型的位置,第二个where
查询返回的数据中没有类型联系的位置。如何查询所有药房和企业以及一组过滤的联系人?
答案 0 :(得分:2)
链接where
这样的调用将导致SQL级别的AND
。 where
可以使用原始SQL作为参数,您可以在其中显式添加OR
,但正确参数化是相当混乱的IMO(尽管可以这样做)。因此,对于这种类型的查询,我认为最好使用带有清理输入的原始SQL(以防止SQL注入)。
即。像这样的东西:
x = ActiveRecord::Base.connection.raw_connection.prepare(
"SELECT * FROM locations
WHERE location_type IN ('business', 'dispensary') OR
(location_type = 'contact' AND owner_id = ?)")
x.execute(params[:id])
x.close
这将从locations
表格中选择location_type
为商业'或者'药房',无论owner_id
,和所有与location_type is 'contact' where the
owner_id`匹配的项目都与传入的项目相匹配。
根据OP的评论进行编辑:
我倾向于更喜欢原始SQL来处理更复杂的查询,因为我发现更容易控制行为(ORM有时会做一些不太理想的事情,例如执行相同的查询1000次以获得1000个条目一次SQL查询一次,导致可怕的性能),但是,如果您希望保持在 ActiveRecord 的范围内,您可以使用带有参数的where
形式。它本身就是一些原始SQL,因为你需要自己指定where子句,但是你不需要获得raw_connection
并明确执行 - 它将在您正在进行的 ActiveRecord 查询的框架。
所以,看起来像这样:
@locations = Location.where("location_type IN ('business', 'dispensary') OR
(location_type = 'contact' AND owner_id = ?)", params[:id])
有关详细信息,请参阅此Active Record guide page,第2.2节。
编辑以回应OP的后续问题:
关于SQL中的?
,您可以将其视为排序的占位符(实际上没有格式化它,而是表示参数在那里)。
重要的是,在查询中放置?
,然后将要使用的实际值作为参数传递给where
(以及某些其他函数)此外,底层SQL驱动程序将以防止 SQL注入的方式将参数插入到查询中,这可能允许各种不同的问题。如果你自己直接对查询字符串进行插值,你仍然可能会受到 SQL注入的影响。因此,不仅?
安全来自SQLI,它还专门用于阻止它。
您的查询中可以有一堆?
,只要您在查询字符串后面传递相应数量的参数作为参数(否则SQL驱动程序应该出错)。