Sql RAILS:where块中的自定义函数
shirt.rb 作为输入,我得到值params[:size],例如等于170。表大小列中的数据以-160-180格式存储,因此它是一个字符串 我如何执行以下查询: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.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