SQL - 在连接表上查询

标签 sql ruby-on-rails postgresql activerecord ruby-on-rails-4

我正在尝试根据与 listing_options 的 has_many 关系查询表 listings。列表选项有不同的类型,我想创建一个 SQL 查询来匹配列表与 listing_options。这是 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])

注意:如果查询的一部分被省略,像这样构造查询会导致返回重复的记录,这可能是问题的一部分。

包含相同的查询(也没有返回结果):

Listing.includes(:listing_options).where("listing_options.option_id IN (?)", [1,2]).references(:listing_options).where("listing_options.option_id IN (?)", [4,5,7])

类(class)列表
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])

最佳答案

查询未返回任何结果,因为您指定了 2 个相反的条件: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

关于SQL - 在连接表上查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24965521/

相关文章:

sql - PostgreSQL 总和类型转换为 bigint

mysql - 动态分区 + 在 HIVE 上创建为

ruby-on-rails - Rails 中来自关联的数据透视表属于_to - has_many

ruby-on-rails - 使用 Ruby Win32ole 发送格式化/html Outlook 电子邮件

mysql - 类(class)明智,具有最大总分的学生姓名

sql - 基于postgres中的列类别的分层抽样

ruby-on-rails - ActiveModel::Error :有没有办法获取错误类型?

postgresql - 使用 PostgreSQL,如何表达两个时间范围在其他条件成立的情况下不重叠的表约束?

ruby-on-rails-3 - 如何让 rails/pg/postgresql 寻找不同的主机名/套接字?

sql - Postgresql 加入 2 个具有 km 范围的表