【问题标题】:a proper way to escape %% when building LIKE queries in Rails 3 / ActiveRecord在 Rails 3 / ActiveRecord 中构建 LIKE 查询时转义 %% 的正确方法
【发布时间】:2011-08-08 06:27:12
【问题描述】:

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

【问题讨论】:

  • 我希望您没有任何将帖子扩展到数百万个帖子的计划。该查询会比 SUV 从油箱中排放汽油更快地消耗系统资源。
  • @Wes:取决于您的数据库。 AFAIK,最新版本的 PostgreSQL 可以为使用前缀的 LIKE 匹配使用索引(即,对于某些固定的X,格式为 X% 的匹配)。这里有一些注释:stackoverflow.com/questions/1566717/…
  • @Wes:尽管如此,你仍然被%X% 模式所困扰,这几乎肯定会给你一个表格扫描。抱歉,我没有权威的参考资料,但您可能会摸索一个查询计划,看看会发生什么。

标签: sql ruby-on-rails ruby-on-rails-3 rails-activerecord


【解决方案1】:

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

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

并像这样调用范围:

Account.search('Test_%')

得到的转义 sql 字符串是:

SELECT "accounts".* FROM "accounts" WHERE ("accounts"."name" LIKE 'Test\_\%%')

在此处阅读更多信息:http://edgeapi.rubyonrails.org/classes/ActiveRecord/Sanitization/ClassMethods.html

【讨论】:

  • 在我看来,这是解决这个问题最优雅的方法
  • Guess from rails 5.* sanitize_sql_like is not supported 还有其他选择吗?
  • 仅供参考,这些是 5.0 和 5.1 上的受保护/私有方法。它们仅在 5.2 上公开。
  • @Teoulas 是对的。 Rails 5.0 和 5.1,用这个来调用它们:ActiveRecord::Base.send(:sanitize_sql_like, "Test_%")
【解决方案2】:

如果我理解正确,您担心 some_url 中出现“%”,这是正确的;您还应该担心嵌入的下划线(“_”),它们是“。”的 LIKE 版本。在正则表达式中。我不认为有任何特定于 Rails 的方法可以做到这一点,所以你只剩下gsub

.where('url like ?', some_url.gsub('%', '\\\\\%').gsub('_', '\\\\\_') + '%')

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

您应该检查您的日志以确保两个反斜杠通过。检查irb 中的内容时,我得到了令人困惑的结果,使用五个(!)得到正确的输出,但我看不出它的意义;如果有人确实看到其中五个的意义,将不胜感激。

更新:Jason King 为逃脱角色的噩梦提供了简化。这使您可以指定 temporary escape character,以便您可以执行以下操作:

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

我还切换到 gsub 的块形式,让它不那么讨厌。

这是标准 SQL92 语法,因此适用于任何支持该语法的数据库,包括 PostgreSQL、MySQL 和 SQLite。

将一种语言嵌入到另一种语言中总是有点像噩梦般的拼凑,而且您无能为力。总会有一些丑陋的东西,你只需要微笑和忍受。

【讨论】:

  • 顺便说一句,如果(我不知道)您需要在这些字符串中添加双反斜杠,则上述内容不会这样做。 `'\\' => ` 在 Ruby(和大多数语言)中。
  • @Jason:五次转义在irb 中产生了正确的结果,感谢您了解这一点。我猜它作为三个标记“\\”,“\\”和“\%”经过转义处理后,根据需要最终成为“\\%”。我通常只是继续添加它们,直到它起作用,然后对理由进行逆向工程。有谁知道一个真正产生文字结果而无需任何处理或一大堆混乱的报价运算符?
  • 您可以做的其他事情是添加一个ESCAPE 子句,如下所示:.where( "url LIKE ? ESCAPE '!'", some_url.gsub('%', '!%').gsub('_', '!_')
  • @Jason:真是天才。我希望我能给你点评论或只是将答案的所有权转让给你。
  • 您也可以使用捕获组而不是块语法(更短,可能更快):.where("url LIKE ? ESCAPE '!'", some_url.gsub(/([!%_])/, '!\1')
【解决方案3】:

https://gist.github.com/3656283

使用此代码,

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

在“sample”和“code”之间正确转义%,并匹配“AAAsample%codeBBB”,但至少在 MySQL、PostgreSQL 和 SQLite3 上不匹配“AAAsampleBBBcodeCCC”。

【讨论】:

    【解决方案4】:
    Post.where('url like ?', "%#{some_url + '%'}%)
    

    【讨论】:

    • @Sam 非常接近。 Post.where('url like ?', "%#{some_url}%")
    • 您的代码只是使类似条件与其他条目匹配。 @Costa-Shaprio 正在寻找一种方法来做你正在做的事情加上 % 或者至少这就是我的阅读方式,即“可能包含百分号”
    • @Rein:不。这会改变匹配的性质(“开始于”变成“任何地方”)并且没有任何作用来解释 some_url 中嵌入的“%”,更不用说下划线了。
    • @mu 在您发表评论之前,您是否检查过 ActiveRecord 的 sql 清理是否已经逃脱了 %s?
    • @Rein:当a 恰好是未转义的'x%x' 时,AR 如何区分有意的%x%x% 和意外的"%#{a}%"?第一个(有意的)案例想要“anything x anything x anything”,而第二个案例想要“anything x %x anything" 但得到 "anything x anything x anything" 因为没有人逃脱它。当 AR 看到 LIKE 的参数时,它只有一个嵌入了 SQL 语法的字符串,因此推断程序员的意图为时已晚。
    猜你喜欢
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-06
    • 2015-04-20
    相关资源
    最近更新 更多