我有一张没有身份证的桌子。既然我知道这是一个错误的设计,我想在该表中添加一个主键ID。这是脚本:
def up
add_column :players, :id, :primary_key, first: true
change_table :players do |t|
t.change :id, :uuid
end
end
def down
remove_column :players, :id
end
问题是脚本为UUID列生成整数值。如何正确地将主键UUID添加到表中?
编辑: 它已经投入生产。 我可以添加ID列,但它会填充整数,而不是我预期的UUID。
答案 0 :(得分:1)
在数据库环境中,列id为主键的表将是表的每一行的唯一标识符。它通常是带有自动增量的整数值(但不是必需的),通常使用32位或64位整数。表中没有2行具有相同的ID。
在Rails中, UUID 是全局唯一标识符。它可用于识别用户,会话,任何内容。并且id适用于所有类别。它是使用随机生成器创建的,因此为了减少为2个不同创建具有相同UUID的机会,它是128位值。
有很多方法可以将UUID用作主键,但它可能是一个糟糕的设计理念,因为数据库引擎必须根据它来查找行,因为它很大,找到行的工作可能会更难。通常,DB会将UUID转换为内部表行id。
我猜你需要一个主键和一个单独的UUID列,但这取决于上下文。
答案 1 :(得分:0)
这是完全可行的,在许多情况下,这是一个不错的设计选择。创建分片,进行多主机写入或需要移至任何多数据库系统时,创建全局唯一ID会使事情变得更加容易。当然,缺点是它占用了bigint两倍的空间,并且比自动增量更复杂。
首先,切勿将UUID存储为VARCHAR。好的,现在已经解决了,下面是一些代码:
此gem使用Mysql 8.0+的UUID_TO_BIN和BIN_TO_UUID来存储二进制文件并检索UUID字符。 https://github.com/nedap/mysql-binuuid-rails
gem 'mysql-binuuid-rails'
在模型中,使用Rails 5+中的Attributes API。确保记得设置所有外键来解码UUID,即attribute :user_id, MySQLBinUUID::Type.new
class Account < ApplicationRecord
attribute :id, MySQLBinUUID::Type.new
before_create { self.id = ApplicationRecord.generate_uuid }
end
此迁移指定不创建默认主键,然后手动指定主键。触发器很不错,因为如果您运行任何没有ActiveRecord来创建记录的查询,它仍会正确生成并填充主键。
class CreateAccounts < ActiveRecord::Migration[6.0]
def change
create_table :accounts, id: false do |t|
t.binary :id, limit: 16, primary_key: true
t.string :email
t.timestamps
end
reversible do |dir|
dir.up do
execute <<~SQL
CREATE TRIGGER before_insert_set_account_uuid
BEFORE INSERT ON accounts
FOR EACH ROW
BEGIN
IF new.id IS NULL THEN
SET new.id = UUID_TO_BIN(UUID(), 1);
END IF;
END
SQL
execute "UPDATE accounts SET id = UUID_TO_BIN(UUID());"
end
dir.down do
execute <<~SQL
DROP TRIGGER before_insert_set_account_uuid;
SQL
end
end
end
end
这是使用MySQL的方法来创建UUID。请注意,这意味着要进行两次数据库访问,一次是获取新的UUID,另一次是保存记录。最好利用UUIDTools等在本地生成它。
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
def self.generate_uuid
ActiveRecord::Base.connection.execute("select UUID();").first[0]
end
end