【问题标题】:sqlite3 varchar matching with "like" but not "="sqlite3 varchar 匹配“like”但不匹配“=”
【发布时间】:2011-09-19 08:28:10
【问题描述】:

使用 Rails 3.1 和 sqlite3 开发、测试环境。

在迁移中添加了一个新表:

create_table :api_keys do |t|
  t.string :api_key
  t.integer :user_id
  t.timestamps
end

这会生成一个具有以下架构的表:

create_table "api_keys", :force => true do |t|
  t.string   "api_key"
  t.integer  "user_id"
  t.datetime "created_at"
  t.datetime "updated_at"
end

在 ActiveRecord 模型中:

before_create :fill_api_key

private

def fill_api_key
  self.api_key = SecureRandom.hex(30)
end

ActiveRecord 的动态查找方法find_by_api_key(api_key) 不起作用(返回 nil)。与:

ApiKey.where({:api_key => 'something'}).first

在 sqlite3 中,我执行以下操作:

insert into api_keys (id, api_key) values (-1, '12345');

如果我现在运行选择:

select api_keys.* from api_keys where api_keys.api_key = '12345';

会找到记录。

如果我运行未过滤的选择,则会显示从我的应用创建的预先存在的数据:

select api_keys.* from api_keys;

如果我尝试通过将来自这些预先存在的记录之一的长十六进制字符串粘贴到我的查询中来查找预先存在的记录:

select api_keys.* from api_keys where api_keys.api_key = 'long hex string';

然后它不返回任何结果。如果我尝试这样做:

select api_keys.* from api_keys where api_keys.api_key like 'long hex string';

然后我得到一个匹配。

我在 api_keys.api_key 上创建了一个索引,但没有任何效果。

此问题会影响我的应用程序中的另一个模型,该模型使用 Digest::SHA1::hexdigest 生成类似的随机十六进制数字字符串。

詹姆斯

【问题讨论】:

  • 我应该补充一点,这适用于我提到的第二个模型类,直到升级到 Rails 3.1。那时测试开始失败。

标签: sqlite ruby-on-rails-3.1


【解决方案1】:

好的,我想我已经想通了。问题不在于这是 Rails 3.1,而在于您可能已经从 Ruby 1.8.7 迁移到 Ruby 1.9.2。

在 Ruby 1.9 中,所有字符串现在都进行了编码。默认情况下,所有字符串都应为UTF-8,但是,SecureRandom.hex(30) 返回编码为ASCII-8BIT

您可以使用以下命令在 sqlite3 中确认这一点:.dump api_keys,您可能会看到 api_key 字段如下所示:

INSERT INTO "api_keys" VALUES(1,X'376433356530[...]',1);    
INSERT INTO "api_keys" VALUES(1,'1234567890[...]',1);

第一个是SecureRandom生成的api_key。第二个是通过在控制台中键入创建的。 X 表示该字段被编码为 blob,而不是字符串。

要解决此问题,请将您的 fill_api_key 更改为:

self.api_key = SecureRandom.hex(30).force_encoding('UTF-8')

我刚刚得到了一点重要的时间,所以希望它可以帮助你。

这里有一些关于 1.9 中对 String 的更改的详细信息:http://blog.grayproductions.net/articles/ruby_19s_string

【讨论】:

  • 谢谢!这似乎成功了。我不确定我是如何错过日志文件中的 X 的。
【解决方案2】:

如果您使用的是十六进制数字字符串,那么如果您想使用 where x = y 选择,则必须进行大小写匹配。与某些数据库不同,SQLite 区分大小写。

【讨论】:

  • 谢谢史蒂夫。似乎不是这样——我在 sqlite3 中复制和粘贴字符串——都是小写的,我使用的都是小写,我直接复制文字字符串。
  • 好的,在上面的示例中,您选择的 INSERT 没有字母十六进制数字。如果直接在 SQLite 中执行 INSERT 时使用 '1a3bf7' 或类似的东西会发生什么。 SELECT 找到了吗?你的 Rails 应用程序有吗?另外,这个十六进制字符串有多长?
  • 十六进制字符串的长度为 60 个字符(可能有点矫枉过正)。在我的测试中,我输入了长达 70 个字符的十进制数字字符串,然后开始添加字母(小写,在开头)最多 80 个字符。
  • 我首先要说我很难过。但是,如果您执行 rake db:drop && rake db:create 会发生什么,然后尝试相同的操作。我只是在这里猜测,但是关于字母字符的解释方式似乎有些奇怪。
  • 我提到的第二个表包含升级到 Rails 3.1 前后的数据。可以使用 select ... where field = 'value' 找到更新之前的记录,但之后创建的记录不能(但“喜欢”有效)。它似乎与字符串有关。这让我觉得可能与字符编码有关。
猜你喜欢
  • 2014-05-03
  • 1970-01-01
  • 1970-01-01
  • 2011-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多