如何使用rails和sql动态处理嵌套的where和/或查询

cqoc49vn  于 2021-08-09  发布在  Java
关注(0)|答案(1)|浏览(351)

我目前正在构建一个特性,它要求我在哈希上循环,并为哈希中的每个键动态修改一个sql查询。
实际的sql查询应该如下所示:

select * from space_dates d
    inner join space_prices p on p.space_date_id = d.id
    where d.space_id = ?
    and d.date between ? and ?
    and (
        (p.price_type = 'monthly' and p.price_cents <> 9360) or
        (p.price_type = 'daily' and p.price_cents <> 66198) or
        (p.price_type = 'hourly' and p.price_cents <> 66198) # This part should be added in dynamically
    )

最后 and 查询是动态添加的,如您所见,我基本上只需要其中一个条件为真,而不是所有条件。

query = space.dates
      .joins(:price)
      .where('date between ? and ?', start_date, end_date)

# We are looping over the rails enum (hash) and getting the key for each key value pair, alongside the index

SpacePrice.price_types.each_with_index do |(price_type, _), index|
  amount_cents = space.send("#{price_type}_price").price_cents
  query = if index.positive? # It's not the first item so we want to chain it as an 'OR'
            query.or(
              space.dates
               .joins(:price)
               .where('space_prices.price_type = ?', price_type)
               .where('space_prices.price_cents <> ?', amount_cents)
             )
           else
             query # It's the first item, chain it as an and
               .where('space_prices.price_type = ?', price_type)
               .where('space_prices.price_cents <> ?', amount_cents)
           end
end

rails中的输出是:

SELECT "space_dates".* FROM "space_dates"
  INNER JOIN "space_prices" ON "space_prices"."space_date_id" = "space_dates"."id"
  WHERE "space_dates"."space_id" = $1 AND (
   (
     (date between '2020-06-11' and '2020-06-11') AND
     (space_prices.price_type = 'hourly') AND (space_prices.price_cents <> 9360) OR
     (space_prices.price_type = 'daily') AND (space_prices.price_cents <> 66198)) OR
     (space_prices.price_type = 'monthly') AND (space_prices.price_cents <> 5500)
   ) LIMIT $2

这和预期的不一样。为了产生相同的输出,我需要将最后几行放在另一组圆括号中。我不知道如何使用activerecord。
我不可能用 find_by_sql 因为这也是动态生成的sql。

qlfbtfca

qlfbtfca1#

所以,我用arel和rails在一个小时内解决了这个问题

dt = SpaceDate.arel_table
 pt = SpacePrice.arel_table

 combined_clauses = SpacePrice.price_types.map do |price_type, _|
   amount_cents = space.send("#{price_type}_price").price_cents
   pt[:price_type]
     .eq(price_type)
     .and(pt[:price_cents].not_eq(amount_cents))
 end.reduce(&:or)

  space.dates
    .joins(:price)
    .where(dt[:date].between(start_date..end_date).and(combined_clauses))
  end

sql输出为:

SELECT "space_dates".* FROM "space_dates"
INNER JOIN "space_prices" ON "space_prices"."space_date_id" = "space_dates"."id" 
WHERE "space_dates"."space_id" = $1
AND "space_dates"."date" BETWEEN '2020-06-11' AND '2020-06-15'
AND (
  ("space_prices"."price_type" = 'hourly'
   AND "space_prices"."price_cents" != 9360
   OR "space_prices"."price_type" = 'daily'
   AND "space_prices"."price_cents" != 66198)
  OR "space_prices"."price_type" = 'monthly'
  AND "space_prices"."price_cents" != 5500
) LIMIT $2

我最后做的是:
基于枚举键和 price_cents 减少子句并使用 or 使用 and 操作员和 combined_clauses

相关问题