我正在尝试根据与listing_options的has_many关系查询表,列表。列表选项有不同的类型,我想创建一个可以与listing_options匹配列表的SQL查询。这是SQL查询atm:
SELECT "listings".* FROM "listings" INNER JOIN "listing_options" ON
"listing_options"."listing_id" = "listings"."id" WHERE ("listings".state IS NULL) AND
(listing_options.option_id IN ('1','2')) AND
(listing_options.option_id IN ('4','5','7')) LIMIT 12 OFFSET 0
假设我有两个列表,分别带有选项值(1,5)和(1,6)。我希望查询返回第一个列表而不是第二个列表。问题是查询一次只考虑一个选项值,即选项值不能同时为1和5,因此查询返回空。
ActiveRecord生成SQL:
Listing.joins(:listing_options).where("listing_options.option_id IN (?)", [1,2]).where("listing_options.option_id IN (?)", [4,5,7])
注意:如果省略查询的一部分,则会像这样构造查询会导致返回重复的记录,这可能是问题的一部分。
与includes相同的查询(也没有返回结果):
Listing.includes(:listing_options).where("listing_options.option_id IN (?)", [1,2]).references(:listing_options).where("listing_options.option_id IN (?)", [4,5,7])
班级列表
has_many:listing_options
has_and_belongs_to_many:options,join_table :: listing_options
类选项
belongs_to:option_type,inverse_of :: option
我尝试反转查询无效(尽管这样做也会更复杂,因为我需要指定option_ids以避免基于选项类型)。
示例:
Listing.includes(:listing_options).where("listing_options.option_id NOT IN (?)", [3,6,8]).references(:listing_options)
此查询返回列表 - 超出应有的范围。对于大量列表和最多四种类型的列表选项(对列表选项的附加查询),查询应该是合理有效的。出于这个原因,我想只做一个查询而不是重复查询结果。
编辑 - 解决方案 - 来自@ErwinBrandstetter的SQL编写为搜索范围
.where('EXISTS (SELECT 1 FROM listing_options o1 JOIN listing_options o2 USING (listing_id) WHERE o1.listing_id = id AND o1.option_id IN (?) AND o2.option_id IN (?))', [1,2], [4,5,7])
答案 0 :(得分:3)
查询未返回任何结果,因为您指定了两个相反的条件:listing_options.option_id IN ('1','2')
和listing_options.option_id IN ('4','5','7')
。当您连接表时,where
子句将单独应用于连接表的每个组合。
要获得您想要的结果(所有具有不同选项的列表),您需要多次加入listing_options表,如下所示:
SELECT l.* FROM listings l
INNER JOIN listing_options o1 ON l.id = o1.listing_id
INNER JOIN listing_options o2 ON l.id = o2.listing_id
WHERE l.state IS NULL
AND o1.option_id IN ('1','2')
AND o2.option_id IN ('4','5','7')
或者您可以使用子选择,如下所示:
SELECT * FROM listings
WHERE state IS NULL
AND id IN (SELECT listing_id FROM listing_options WHERE option_id IN ('1','2'))
AND id IN (SELECT listing_id FROM listing_options WHERE option_id IN ('4','5','7'))
如您所见,使用ActiveRecord
更容易生成第二个查询,特别是如果您的选项数量是动态的:
options = [[1,2], [4,5,7]]
query = Listing.where(state: nil)
options.each do |array|
query = query.where('id IN (SELECT listing_id FROM listing_options WHERE option_id IN (?))', array)
end
query.load
答案 1 :(得分:1)
我建议使用EXISTS
半连接,其中listing_options
加入自身(根据需要多次加入)。
SELECT *
FROM listings l
WHERE state IS NULL
AND EXISTS (
SELECT 1
FROM listing_options o1
JOIN listing_options o2 USING (listing_id)
WHERE o1.listing_id = l.id
AND o1.option_id IN (1,2)
AND o2.option_id IN (4,5,7)
);
您只能使用@rabusmar's first query(或" 6)Sean "中演示的多个联接。在下面的链接答案中) if 每组最多只能有一个匹配option_id
。否则你会乘以行,这需要重新分组。昂贵的,不称职的工作。
EXISTS
通常比IN
更快,同时也不会出现NULL
值的棘手行为。
我们在这个相关问题下汇集了关系师的技术库: