能否执行原生SQL查询、使用预处理语句且不使用ActiveRecord::Relation::QueryAttribute?
嘿,针对你提出的两个Rails数据库操作问题,我整理了实用的解决方案,完全避开你不想用的ActiveRecord::Relation::QueryAttribute类:
Rails原生SQL与Upsert解决方案(避开ActiveRecord::Relation::QueryAttribute)
问题1:执行原生SQL查询与预处理语句(不依赖ActiveRecord::Relation::QueryAttribute)
当然可以!你直接借助ActiveRecord的数据库连接对象的底层方法就能实现,完全不需要用到ActiveRecord::Relation::QueryAttribute。这里有两种常用方式:
方式一:使用prepare + execute
这种方式适合重复执行的查询,手动创建预处理语句,绑定参数后执行:
# 创建预处理语句(PostgreSQL用$1/$2占位符,MySQL用?) stmt = ActiveRecord::Base.connection.prepare("get_user_by_id", "SELECT * FROM users WHERE id = $1") # 执行语句并传入参数 user_results = stmt.execute(current_user_id) # 记得用完关闭语句(可选,连接回收时会自动处理,但显式关闭更规范) stmt.close
方式二:使用exec_query直接执行带参数的SQL
如果是一次性查询,用exec_query更简洁,参数通过数组传递,数据库会自动处理预处理:
results = ActiveRecord::Base.connection.exec_query( "SELECT name, email FROM users WHERE created_at > $1", "Fetch Recent Users", # 可选的查询名称,用于日志 [[nil, 7.days.ago]] # 参数数组:每个元素是[类型标识, 值],nil让数据库自动推断类型 )
这两种方法都是直接和数据库驱动交互,参数绑定过程完全绕开了ActiveRecord::Relation::QueryAttribute类。
问题2:带预处理语句的Upsert操作(ON CONFLICT DO NOTHING)
你提到的ON CONFLICT语法(PostgreSQL专属,MySQL用ON DUPLICATE KEY UPDATE)可以通过数据库连接的底层方法实现,同样不需要依赖ActiveRecord::Relation::QueryAttribute。这里推荐两种可靠的写法:
方式一:用prepare + execute实现可复用的Upsert
如果需要多次执行相同的Upsert逻辑,预处理语句能提升性能:
# 创建Upsert预处理语句 upsert_stmt = ActiveRecord::Base.connection.prepare( "upsert_foos", "INSERT INTO foos (thing_id, bar_id) VALUES ($1, $2) ON CONFLICT (thing_id, bar_id) DO NOTHING" ) # 执行Upsert,传入具体参数 upsert_stmt.execute(1, 2) # 也可以批量传入参数(如果驱动支持) upsert_stmt.execute(3, 4) # 关闭语句 upsert_stmt.close
方式二:用exec_insert一次性执行Upsert
如果是单次操作,exec_insert更方便,它会自动处理参数绑定,还能返回插入结果的主键(如果需要的话):
# 定义参数 thing_id = 1 bar_id = 2 # 执行Upsert ActiveRecord::Base.connection.exec_insert( "INSERT INTO foos (thing_id, bar_id) VALUES ($1, $2) ON CONFLICT (thing_id, bar_id) DO NOTHING", "Upsert Foo", # 日志用的查询名称 [[nil, thing_id], [nil, bar_id]] # 参数数组,nil自动推断类型 )
这两种写法都直接使用数据库连接的原生预处理能力,完全不需要借助ActiveRecord::Relation::QueryAttribute来包装参数。
内容的提问来源于stack exchange,提问作者John Bachir
相关产品推荐
相关产品推荐

