【问题标题】:Rails SQL regular expressionRails SQL 正则表达式
【发布时间】:2013-11-15 15:53:00
【问题描述】:

我正在尝试搜索 A0001、A0002、A1234、A2351 等系列中的最大数字...问题是我正在搜索的列表中也有字符串,例如 AG108939、E092357、AL399、 22-30597等...

所以基本上,我想要我的数据库中的最高 A#### 值。我正在使用以下查询:

@max_draw = Drawing.where("drawing_number LIKE ?", "A%")

在 AG309 等数字开始出现问题之前,它一直有效,因为它以 A 开头,但格式与我正在寻找的不同。

我假设这对于正则表达式应该非常简单,但我是新手,不知道如何使用正则表达式正确编写此查询。以下是我尝试过的一些只返回 nil 的方法:

 @max_draw = Drawing.where("drawing_number LIKE ?", /A\d+/)
 @max_draw = Drawing.where("drawing_number LIKE ?", "/A\d+/")
 @max_draw = Drawing.where("drawing_number LIKE ?", "A[0-9]%")

【问题讨论】:

    标签: ruby-on-rails activerecord sqlite expression


    【解决方案1】:

    在带有 Postgres 数据库的 Rails 4+ 上,RegEx 查询的一般形式是:

    Model.where("column ~* ?", 'regex')
    

    至于正则表达式,它可以是一般的'^A\d+$' 或更具体的'^A\d{4}$' 分解:

    ^ - string start anchor
    A - literal "A"
    \d+ - one or more digits (0-9)
    \d{4} - exactly four digits
    $ - string end anchor
    

    基本上,正则表达式读取“字符串应以 A 开头,后跟四位数字,然后字符串应结束”。 最后的查询行是:

    @max_draw = Drawing.where("drawing_number ~* ?", '^A\d{4}$')
    

    RubyDoc 或更易于访问的Perl variant 上进一步阅读ruby RegEx(由Sublime text 使用)

    【讨论】:

    • 为了完整起见(因为我刚刚遇到):使用~* 表示不区分大小写的正则表达式,使用~ 表示区分大小写。对于否定,只需在前面加上一声 !!~!~*
    • 奇怪的是,只有在“正则表达式”而不是“正则表达式”(单引号)时才有效
    • 是的,Ruby 字符串中的正则表达式存在问题,即“\d”的计算结果为“d”。单引号确实看起来更安全,但遗憾的是排除了插值。
    • @Epigene 似乎是因为双引号会被转义。您可能只使用('\a' + variable.to_s + '\b')to_s 可能很重要,因为它不是插值,如果类型转换问题不够“粘稠”,它可能会出错。
    • 这个问题是用 sqlite 标记的,所以我认为这不是 100% 的最佳答案......但这正是我对 postgres 所需要的,所以感谢您的回答!
    【解决方案2】:

    你做得很好!缺少的是 REGEXP 函数,该函数用于查询中的正则表达式:

    所以在你的情况下使用

    Drawing.where("drawing_number REGEXP ?", 'A\d{4}')
    # the {4} defines that there have to be exactly 4 numbers, change if you need to
    

    在 SQL 中,您使用 '-colons,这很奇怪,因为您通常以 /-backslashes 开始正则表达式

    【讨论】:

    • 我试过这个并且它抛出一个错误:ActiveRecord::StatementInvalid in DrawingsController#new SQLite3::SQLException: near "REGEX": syntax error: SELECT MAX("drawings"."drawing_number") AS max_id FROM "drawings" WHERE (drawing_number REGEX 'A\d{4}')
    • 我一开始有错别字,忘记了REGEXP的P
    • 如果你的SQLite没有默认安装REGEXP,可能会出现另一个问题。 stackoverflow.com/questions/5071601/…
    • 嗯。它仍然抛出一个错误,但这次它说: SQLite3::SQLException: no such function: REGEXP
    • 所以上面的代码可以工作,但是你知道,它开始将 REGEXP 识别为有效,但是在编写一个好的查询时遇到了问题,说 A 处存在语法错误
    【解决方案3】:

    您不能在 SQL 中使用正则表达式,而这正是您想要做的。最好的办法是像原始代码一样只选择以 A 开头的条目,然后跳过开头有多个字母的条目。

    items = Drawing.where( [ 'drawing_number LIKE ?' , 'A%' ] )
    max_value = 0
    items.each do |item|
      next if item.drawing_number =~ /\A[A-Za-z]{2,}/
      drawing_number = item.drawing_number.gsub(/\AA/, '').to_i
      max_value = drawing_number if drawing_number > max_value
    end
    

    我有理由确定它可以缩短,但这应该可以满足您的需要。

    (\A 是行锚的开始,适用于包含换行符的字符串)

    ({2,} 匹配两个或多个前面的字符范围)

    http://www.rubular.com/ 非常适合测试 ruby​​ 正则表达式。

    【讨论】:

    • 谢谢!像魅力一样工作。
    • @Xathras - “你不能在 SQL 中使用正则表达式”? PostgreSQLMySQL 都支持正则表达式
    • ^^^ 但是使用正则表达式是否有效(比如次线性时间)? @亚林
    • @Nuclearman 我在 MySQL 中使用了正则表达式搜索,其速度几乎与等式匹配一样快,即使使用大表也是如此。它可能取决于索引。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-06
    • 2021-05-22
    • 2014-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-02
    相关资源
    最近更新 更多