MySQL / Postgres之间的大小写敏感问题

时间:2013-08-24 22:59:53

标签: mysql ruby-on-rails postgresql heroku rails-activerecord

当用户注册我的应用程序以查看用户名是否已存在时,我正在进行ajax验证。每当您取消焦点/模糊用户名字段时,都会发出请求。本地我使用MySQL和Heroku它是Postgres,所以我假设问题存在于那里,因为它在我的开发机器上完全本地运行。

注册的用户名为 Matt ,例如......

发展:

  • Matt = take is true
  • matt = take is true
  • MATT =采用是真的
  • MaTt =拍摄是真的等等......

生产:

  • Matt = take is true
  • matt = take is false
  • MATT = take is false
  • MaTt = take is false

这是方法(你甚至可以看到我更进一步试图强迫它倒闭,再次在本地工作它应该如何,但不是生产)......

def checkname

  scrubb = ActionController::Base.helpers.sanitize(params[:username], :tags => '')
  user = User.find_by_username(scrubb, :conditions => [ "lower(username) = ?", scrubb.downcase ])

  if user.blank?
    render :json => { :isAvailable => true }
  else
    render :json => { :isAvailable => false }
  end

  return

end

**编辑* *

这是生成的MySQL查询的输出:

SELECT `users`.* FROM `users` WHERE `users`.`username` = 'MATT' AND (lower(username) = 'matt') LIMIT 1

因此,看起来真正的问题是AND语句Rails正在生成。我该如何删除它?

2 个答案:

答案 0 :(得分:1)

您使用了错误的方法来检查用户名。这样:

User.find_by_username(scrubb)

只是一种产生这种SQL的方法:

select * from users where username = ...

然后添加一些:conditions

:conditions => [ "lower(username) = ?", scrubb.downcase ]

find_by_username只是将它们添加到通常使用的WHERE子句中。结果是:

User.find_by_username(scrubb, :conditions => ...)

表示:

  

找到Userusernamescrubb为真的:conditions

在MySQL中,字符串比较:

`users`.`username` = 'MATT'

在您的配置中显然不区分大小写(我相信默认配置),但它在PostgreSQL中区分大小写;如果username'Matt',那么在PostgreSQL中该条件将失败,除了区分大小写的匹配之外,您将找不到任何其他内容。

请勿使用find_by_username(或其他任何内容,如果您希望username不区分大小写),请使用wherecount

n = User.where('lower(username) = ?', scrubb.downcase).count

或更好,whereexists?

taken = User.where('lower(username) = ?', scrubb.downcase).exists?

将范围添加到User似乎也是一个好主意:

class User < ActiveRecord::Base
  def self.with_username(username)
    where('lower(username) = ?', username)
  end    
end

然后你可以说:

how_many = User.with_username(scrubb).count
taken    = User.with_username(scrubb).exists?

并且你不必担心其他任何地方的这种不区分大小写的问题。

请在开发和制作中使用相同的堆栈,还有各种其他的小差异会让你感到悲伤。

答案 1 :(得分:0)

我实际上通过自己的一些研究来解决这个问题,这对我来说更有意义。认为它可能会帮助其他人。

username = params[:username]
username.downcase
@user = User.where(User.arel_table[:username].matches("%#{username}%")).first

使用ArelmatchesILIKE运算符用于Postgres,LIKE用于其他所有运算符(source)。然后,调用first,以便您收到用户的单个对象而不是集合(source)。

完全适用于MySQL和Postgres中的应用方式。我理解,拥有不同的生产和开发环境是不好的做法...... moral of the story。我很快就会解决这个问题。

注意:关于不区分大小写的主题,我还遇到了一个非常有用的宝石,名为route_downcaser,您可能会感兴趣。