在Rails 3/ActiveRecord中构建LIKE查询时逃避%%的正确方法

Cos*_*iro 20 sql ruby-on-rails ruby-on-rails-3 rails-activerecord

我想将url字段与url前缀(可能包含百分号)匹配,例如.where("url LIKE ?", "#{some_url}%").什么是最多的Rails方式?

mu *_*ort 22

如果我理解正确,你会担心"%"出现在内部some_url和正确的情况下; 你也应该担心嵌入式下划线("_"),它们是"."的LIKE版本.在正则表达式.我不认为有任何特定于Rails的方法,所以你留下gsub:

.where('url like ?', some_url.gsub('%', '\\\\\%').gsub('_', '\\\\\_') + '%')
Run Code Online (Sandbox Code Playgroud)

这里也不需要字符串插值.您需要加倍反斜杠以从数据库的字符串解析器中删除它们的含义,以便LIKE解析器看到简单的"\%"并知道忽略转义的百分号.

您应该检查日志以确保两个反斜杠通过.我在检查内容时遇到了令人困惑的结果irb,使用五个(!)得到正确的输出,但我没有看到它的意义; 如果有人确实在其中五个中看到了意义,那么解释性评论将会受到赞赏.

更新:Jason King善意为逃脱逃脱角色的噩梦提供简化.这允许您指定临时转义字符,以便您可以执行以下操作:

.where("url LIKE ? ESCAPE '!'", some_url.gsub(/[!%_]/) { |x| '!' + x })
Run Code Online (Sandbox Code Playgroud)

我也改用了块形式,gsub使它不那么讨厌.

这是标准的SQL92语法,因此可以在任何支持它的数据库中使用,包括PostgreSQL,MySQL和SQLite.

将一种语言嵌入另一种语言中总是有点噩梦般的克服,而且你无法做到这一点.总是会有丑陋的小点,你只需要笑着忍受.

  • 你可以做的其他事情是添加一个`ESCAPE`子句,如下所示:`.where("url LIKE?ESCAPE'!'",some_url.gsub('%','!%').gsub('_' ,'!_')` (8认同)
  • 您也可以使用捕获组而不是块语法(更短且可能更快):`.where("url LIKE?ESCAPE'!'",some_url.gsub(/([!%_])/,'!\ 1 ")` (2认同)

phl*_*egx 17

从Rails版本4.2.x开始,有一个名为的活动记录方法sanitize_sql_like.因此,您可以在模型中执行以下搜索范围:

scope :search, -> search { where('"accounts"."name" LIKE ?', "#{sanitize_sql_like(search)}%") }
Run Code Online (Sandbox Code Playgroud)

并调用范围如下:

Account.search('Test_%')
Run Code Online (Sandbox Code Playgroud)

生成的转义sql字符串是:

SELECT "accounts".* FROM "accounts" WHERE ("accounts"."name" LIKE 'Test\_\%%')
Run Code Online (Sandbox Code Playgroud)

在这里阅读更多:http://edgeapi.rubyonrails.org/classes/ActiveRecord/Sanitization/ClassMethods.html

  • 在我看来,这是解决这个问题最优雅的方式 (2认同)
  • @Teoulas 是对的。Rails 5.0 和 5.1,使用它来调用它们:`ActiveRecord::Base.send(:sanitize_sql_like, "Test_%")` (2认同)

小智 6

https://gist.github.com/3656283

有了这段代码,

Item.where(Item.arel_table[:name].matches("%sample!%code%"))

正确地%在"sample"和"code"之间转义,并且匹配"AAAsample%codeBBB",但至少不支持MySQL,PostgreSQL和SQLite3上的"AAAsampleBBBcodeCCC".