Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql RAILS:where块中的自定义函数_Sql_Ruby On Rails_Ruby_Where - Fatal编程技术网

Sql RAILS:where块中的自定义函数

Sql RAILS:where块中的自定义函数,sql,ruby-on-rails,ruby,where,Sql,Ruby On Rails,Ruby,Where,shirt.rb 作为输入,我得到值params[:size],例如等于170。表大小列中的数据以-160-180格式存储,因此它是一个字符串 我如何执行以下查询: Shirt.where("parse_first_number(size) > ? AND parse_second_number(size) < ?", params[:size], params[:size]) 我强烈建议您通过使用两个值来改进数据库模式:min_size和max_size。这样,您就可以进行更简

shirt.rb

作为输入,我得到值params[:size],例如等于170。表大小列中的数据以-160-180格式存储,因此它是一个字符串

我如何执行以下查询:

Shirt.where("parse_first_number(size) > ? AND parse_second_number(size) < ?", params[:size], params[:size]) 

我强烈建议您通过使用两个值来改进数据库模式:min_size和max_size。这样,您就可以进行更简单的查询:

Shirt.where("min_size <= ? AND max_size >= ?", params[:size], params[:size])

您提出的问题是可能的,但要复杂得多。

这基本上是存储数据列类型的问题。如果存储字符串,则数据库无法将其转换为数字并进行比较。一种方法是使用另一个支持范围的数据库,但更方便的方法是使用两列size\u from、size\u to或存储大小的命名变量,并在类似于

class Shirt
  SIZES = [ { large: (160..180), ... ].freeze

让我重申一下,这是一个糟糕的设计,您应该使用单独的列

尽管如此,还没有数据库不可知的解决方案。这是一个给博士后的。请注意,这是丑陋和缓慢的:

size_in_bounds_condition = <<-SQL
  CAST(split_part(coalesce(size, '0-0'), '-', 1) AS integer) < ?
  AND
  CAST(split_part(coalesce(size, '0-0'), '-', 2) AS integer) > ?
SQL

Shirt.where(size_in_bounds_condition, params[:size], params[:size]) 

这也是技术债务的本质。但如果衬衫桌子小的话,现在可能还可以用

shirt_size = 170
shirts = [
  {id:1, size:"160-180", color:"red"},
  {id:2, size:"180-200", color:"red"},
  {id:3, size:"160-180", color:"blue"}
]

shirts.select do |s|
  min, max = s[:size].split("-").map(&:to_i)
  max > shirt_size && min < shirt_size
end

=> [{:id=>1, :size=>"160-180", :color=>"red"}, {:id=>3, :size=>"160-180", :color=>"blue"}] 
Rails在以下位置接受范围作为值:

如果要检查所提供的参数是否在范围内,则只需执行以下操作:

def valid_size?
  sizes = "160-180" # again from your shirt in DB
  min, max = sizes.split('-').collect(&:to_i) # when you get tired of this line, it means it's time to refactor it
  # collapse the following into one line
  params[:size] &&                      # check for presence
    params[:size].between?(min, max) && # check if in range
    params[:size]                       # I guess you want to return the size if valid, rather than only a boolean
end

# Alternative checks, that don't need the presence check:
# - (min..max).member?(params[:size])
# - (min..max).include?(params[:size])
# I choose what I find more explicit and readable
最后,我同意您应该迁移数据库,将最小和最大大小存储为两个独立的整数。所以,像往常一样,为您的函数编写一个测试,然后编写最简单的解决方案,使测试变为绿色。在这一点上,无论如何,继续重构您的数据库:

# migration
def up
  add_column :shirts, :min_size, :integer
  add_column :shirts, :max_size, :integer
  Shirt.find_each do |shirt|
    min, max = shirt.size.split('-').collect(&:to_i) # for the last time :)
    shirt.min_size = min
    shirt.max_size = max
    shirt.save!
  end
  remove_column :shirts, :size
end

def down
  add_column :shirts, :size, :string
  Shirt.find_each do |shirt|
    size = "#{shirt.min_size}-#{shirt.max_size}"
    shirt.size = size
    shirt.save!
  end
  remove_column :shirts, :min_size, :integer
  remove_column :shirts, :max_size, :integer
end

这是个糟糕的设计。使用单独的列。@ndn这是我从以前的开发人员那里得到的,这并不意味着你不应该修复它。如果表中没有那么多数据,迁移将很容易。如果有-你无论如何都不想运行你将要编写的sql怪物。@ndn这是我从以前的开发人员那里得到的,我不想这样存储值。添加新列和迁移现有数据一点也不困难。如果当前有很多其他代码依赖于size属性,这也不容易迁移,尽管我打赌它可能很简单…,那么您可以暂时保留重复的数据。首先添加两个新列db migration,然后运行:Shirt.all.each{| s | s.update_attributesmin_size:s.size.split'-'。首先,max_size:s.size.split'-'。最后}@TomLord在迁移中使用模型会产生一些非常讨厌的相互依赖关系。通常最好将迁移作为纯SQL进行,这样即使模型不再存在,它们也可以在将来运行。@tadman我不是建议在迁移中运行上述代码。我同意这应该是一个单独的一次性rake任务,或者类似的任务。
def valid_size?
  sizes = "160-180" # again from your shirt in DB
  min, max = sizes.split('-').collect(&:to_i) # when you get tired of this line, it means it's time to refactor it
  # collapse the following into one line
  params[:size] &&                      # check for presence
    params[:size].between?(min, max) && # check if in range
    params[:size]                       # I guess you want to return the size if valid, rather than only a boolean
end

# Alternative checks, that don't need the presence check:
# - (min..max).member?(params[:size])
# - (min..max).include?(params[:size])
# I choose what I find more explicit and readable
# migration
def up
  add_column :shirts, :min_size, :integer
  add_column :shirts, :max_size, :integer
  Shirt.find_each do |shirt|
    min, max = shirt.size.split('-').collect(&:to_i) # for the last time :)
    shirt.min_size = min
    shirt.max_size = max
    shirt.save!
  end
  remove_column :shirts, :size
end

def down
  add_column :shirts, :size, :string
  Shirt.find_each do |shirt|
    size = "#{shirt.min_size}-#{shirt.max_size}"
    shirt.size = size
    shirt.save!
  end
  remove_column :shirts, :min_size, :integer
  remove_column :shirts, :max_size, :integer
end