You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Arel中编写`(SELECT @row_index:=-1) AS r`查询语句?

把带MySQL用户变量的查询转换为Arel实现

下面是对应你给出的MySQL查询的Arel实现方案,完全匹配原SQL的逻辑:

1. 定义基础对象与参数

首先绑定Rails模型对应的Arel表,以及传入查询所需的参数:

# 假设你的模型名为 LocationMarket
location_markets_table = LocationMarket.arel_table

# 替换为实际业务中的参数值
target_column = :price # 对应原SQL的 #{column_name}
location_type_val = "city" # 对应原SQL的 #{location_type}
median_type_val = "monthly" # 对应原SQL的 #{type}
# 自定义WHERE条件,示例:location_markets_table[:region].eq("north")
custom_where = location_markets_table[:region].eq("north")

2. 构建子查询(对应原SQL的内层SELECT)

原SQL的子查询核心是实现用户变量@row_index的自增,Arel没有原生支持MySQL变量,直接用SqlLiteral嵌入原生SQL片段即可:

# 生成 @row_index:=@row_index + 1 AS row_index 的Arel表达式
row_index_inc = Arel::Nodes::SqlLiteral.new("@row_index:=@row_index + 1").as("row_index")

# 构建子查询
subquery = location_markets_table.project(
  row_index_inc,
  location_markets_table[target_column]
).where(
  # 拼接所有WHERE条件:自定义条件 + location_type匹配 + 列非空
  custom_where.and(
    location_markets_table[:location_type].eq(location_type_val)
  ).and(
    location_markets_table[target_column].not_eq(nil)
  )
).order(location_markets_table[target_column])

3. 构建主查询(对应原SQL的外层SELECT)

主查询需要关联子查询、计算平均值、初始化变量、设置过滤条件:

# 给子查询设置别名,并创建对应的Arel表对象用于引用列
subq_alias = subquery.as("subq")
subq_table = Arel::Table.new("subq")

# 生成 (SELECT @row_index:=-1) AS r 的Arel表达式
init_row_var = Arel::Nodes::SqlLiteral.new("(SELECT @row_index:=-1)").as("r")

# 生成 FLOOR(@row_index / 2) 和 CEIL(@row_index / 2) 的Arel表达式
floor_expr = Arel::Nodes::NamedFunction.new("FLOOR", [Arel::Nodes::SqlLiteral.new("@row_index / 2")])
ceil_expr = Arel::Nodes::NamedFunction.new("CEIL", [Arel::Nodes::SqlLiteral.new("@row_index / 2")])

# 构建最终的主查询
final_query = Arel::SelectManager.new(Arel::Table.engine)
final_query.project(
  # 计算AVG作为median_value
  Arel::Nodes::NamedFunction.new("AVG", [subq_table[target_column]]).as("median_value"),
  init_row_var,
  # 生成常量值作为median_type
  Arel::Nodes::SqlLiteral.new("'#{median_type_val}'").as("median_type")
).from(subq_alias).where(
  subq_table[:row_index].in([floor_expr, ceil_expr])
)

# 可以输出SQL字符串验证是否和原SQL一致
puts final_query.to_sql

关键说明

  • 对于MySQL特有的用户变量操作,Arel没有封装对应的方法,直接使用Arel::Nodes::SqlLiteral嵌入原生SQL是最简洁的解决方案。
  • 子查询必须通过.as()设置别名,再用Arel::Table.new(别名)来引用子查询的列,这是Arel关联子查询的标准方式。
  • 聚合函数和数学函数(AVG、FLOOR、CEIL)可以用Arel::Nodes::NamedFunction构建,也可以直接用SqlLiteral,前者更贴合Arel的面向对象风格。

内容的提问来源于stack exchange,提问作者user20795798

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 09:40:45