【问题标题】:Rails: How can I take comma separated values and build an SQL query?Rails:如何获取逗号分隔值并构建 SQL 查询?
【发布时间】:2013-09-26 11:17:25
【问题描述】:

使用 Rails 3 Active Record 模型(postgres db)我想获取这样的用户搜索输入:

"some string, some other string, final string"

从那里我想用逗号分割字符串并构建一个看起来像这样的 SQL 查询

SELECT * FROM items WHERE item_name SIMILAR TO '%(some string)%' OR item_name SIMILAR TO '%(some other string)%' OR item_name SIMILAR TO '%(final string)%'

由于我对 Ruby 的语法相当不熟悉,因此我正在努力想出一种构建此查询的方法。

【问题讨论】:

  • str = "some string, some other string, final string".split(',').map{|s| "%(#{s.strip})%"} 然后SIMILAR TO ANY(ARRAY#{str}) 可能有用吗?
  • @j03w:我无法让 SIMILAR TO 与 ANY 一起工作,所以我认为你被困在 LIKE 或跳到真正的正则表达式上。

标签: sql ruby-on-rails ruby postgresql


【解决方案1】:

我会跳过 SIMILAR TO 并直接转到 POSIX regexes,我很确定 SIMILAR TO 会在内部被翻译成正则表达式,那何必呢?此外,~ 将允许您使用ANY 生成一个可读性好的表达式。你可以这样做:

str   = 'some string, some other string, final string'
items = Item.where('item_name ~ any(array[?])', str.split(/\s*,\s*/))

最终会像这样运行 SQL:

select "items".* from "items" where item_name ~ any(array['some string', 'some other string', 'final string'])

这将产生与您的 SIMILAR TO 版本相同的结果,而无需大量字符串争吵。

如果您遇到可以包含正则表达式元字符的 CSV 字符串,那么您可能希望在混合中加入一些转义。反斜杠应该足够安全:

str   = 'some string, some other string, final string'
pats  = str.split(/\s*,\s*/)
           .map { |s| s.gsub(/\p{^Alnum}/) { '\\' + $& } }
items = Item.where('item_name ~ any(array[?])', pats)

切换到 LIKE 也是一种选择,那么您只需要担心_%

str   = 'some string, some other string, final string'
pats  = str.split(/\s*,\s*/)
           .map { |s| s.gsub(/[_%]/, '%' => '\\%', '_' => '\\_') }
           .map { |s| '%' + s + '%' }
items = Item.where('item_name like any(array[?])', pats)

在现实生活中,您会将转义的混乱(以及“添加 LIKE 百分号”的混乱)放入实用程序方法中,以使您的代码更清晰。

如果您不关心大小写,则可以使用~*ilike 进行不区分大小写的模式匹配。

【讨论】:

  • 太棒了,谢谢 - 工作愉快!我应该担心这里的 SQL 注入还是 Rails 为我逃避了它?
  • Rails 在替换 ? 占位符时会处理引用。
【解决方案2】:

试试这个方法,

 string = 'some string, some other string, final string'
 patterns = search_string.split(", ").map{|str| "%#{str}%" }
 items = Item.where('item_name like any(array[?])', patterns)

【讨论】:

    【解决方案3】:

    这应该可行。

    search_string = "some string, some other string, final string"
    
    strings = search_string.split(", ").map{|s| '%' + s + '%'}
    instructions = ''
    strings.each do |string|
      instructions <<  ' OR ' unless instructions == ''
      instructions <<  'item_name like ?'
    end
    DatabaseTable.where(instructions, *strings)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-12
      相关资源
      最近更新 更多