Ruby on rails Rails有多个包含整数主键和字符串外键
我有三个rails对象:Ruby on rails Rails有多个包含整数主键和字符串外键,ruby-on-rails,postgresql,foreign-keys,nosql,Ruby On Rails,Postgresql,Foreign Keys,Nosql,我有三个rails对象:User、DemoUser和Stats。User和DemoUser都有许多与之相关的统计信息。User和Stats表存储在Postgresql上(使用ActiveRecord)。DemoUser存储在redis中。DemoUser的id是一个(随机)字符串。用户的id是一个(标准rails)递增整数 stats表有一个user\u id列,该列可以包含userid或DemoUserid。因此,user\u id列是一个字符串,而不是一个整数 将随机字符串转换为整数不是一种
User
、DemoUser
和Stats
。User
和DemoUser
都有许多与之相关的统计信息。User
和Stats
表存储在Postgresql上(使用ActiveRecord)。DemoUser
存储在redis中。DemoUser
的id是一个(随机)字符串。用户的id
是一个(标准rails)递增整数
stats
表有一个user\u id
列,该列可以包含user
id或DemoUser
id。因此,user\u id
列是一个字符串,而不是一个整数
将随机字符串转换为整数不是一种简单的方法,但将整数id转换为字符串(42->“42”
)是一种非常简单的方法。id保证不会重叠(不会有与DemoUser
具有相同id的User
实例)
我有一些管理这些统计数据的代码。我希望能够传递一个某个用户
实例(可以是DemoUser
或用户
),然后能够使用id
获取统计数据
,更新它们等等。能够为用户
模型定义一个有许多
,也会很好,所以我可以做像user.stats
但是,像user.stats
这样的操作会创建一个类似
从“stats”中选择“stats”。*其中“stats”。“user_id”=42
然后用PG::UndefinedFunction:错误:运算符不存在:字符变化=整数
有没有办法让数据库(Postgresql)或Rails在连接时自动转换ID?(从整数到字符串的转换应该很简单,例如42->“42”
)
编辑:更新问题以尽可能清楚地说明问题。很乐意接受编辑或回答问题以澄清任何问题。您不能在没有内置相等运算符的两种类型之间定义外键
正确的解决方案是将字符串列更改为整数
在您的例子中,您可以为varchar=string
创建一个用户定义的=
操作符,但这会在数据库的其他地方产生混乱的副作用;例如,它将允许伪代码,如:
SELECT 2014-01-02 = '2014-01-02'
没有错误地运行。所以我不会给你代码去做那件事。如果您真的觉得这是唯一的解决方案(我认为这不太可能是正确的),那么请查看和。目前,我的“解决方案”是不在Rails中使用有许多
的辅助函数,但如果需要,我可以在模型中定义一些辅助函数。e、 g
class User < ActiveRecord::Base
# ...
def stats
Stats.where(user_id: self.id.to_s)
end
# ...
end
这应该允许像这样的调用
user.stats
和stats.for_user\u id(user.id)
我想我以前误解了您的问题的一个细节,因为它被隐藏在评论中
(我强烈建议编辑您的问题,以澄清评论显示问题中存在混淆/不完整之处时的要点)
您似乎需要从整数列到字符串列的外键,因为字符串列可能是整数,也可能是一些不相关的字符串。这就是为什么不能将其设置为整数列-它不一定是有效的数值,它可能是来自不同系统的文本键
这种情况下的典型解决方案是使用一个合成主键和两个唯一的
约束,每个系统中的一个用于键,加上一个检查
约束,防止设置这两个约束。例如
CREATE TABLE my_referenced_table (
id serial,
system1_key integer,
system2_key varchar,
CONSTRAINT exactly_one_key_must_be_set
CHECK (system1_key IS NULL != system2_key IS NULL),
UNIQUE(system1_key),
UNIQUE(system2_key),
PRIMARY KEY (id),
... other values ...
);
然后,您可以从整数键控表中获得一个外键引用system1\u key
它并不完美,因为它不能防止相同的值出现在两个不同的行中,一个用于system1\u键
,另一个用于system2\u键
因此,另一种选择可能是:
CREATE TABLE my_referenced_table (
the_key varchar primary key,
the_key_ifinteger integer,
CONSTRAINT integerkey_must_equal_key_if_set
CHECK (the_key_ifinteger IS NULL OR (the_key_ifinteger::varchar = the_key)),
UNIQUE(the_key_ifinteger),
... other values ...
);
CREATE OR REPLACE FUNCTION my_referenced_table_copy_int_key()
RETURNS trigger LANGUAGE plpgsql STRICT
AS $$
BEGIN
IF NEW.the_key ~ '^[\d]+$' THEN
NEW.the_key_ifinteger := CAST(NEW.the_key AS integer);
END IF;
RETURN NEW;
END;
$$;
CREATE TRIGGER copy_int_key
BEFORE INSERT OR UPDATE ON my_referenced_table
FOR EACH ROW EXECUTE PROCEDURE my_referenced_table_copy_int_key();
它复制整数值,如果它是整数,那么您可以引用它
总而言之,尽管我认为整个想法有点不确定。一个选项是在
统计数据表中分别设置用户id
和演示用户id
列。user\u id
将是一个整数,可以用作PostgreSQL中users
表的外键,demo\u user\u id
将是链接到Redis数据库的字符串。如果您想正确处理数据库,您应该使用一个真正的FK将stats.user\u id
链接到users.id
,以确保引用的完整性,并且您应该包含一个检查约束,以确保stats.user\u id
和stats.demo\u user\u id
中正好有一个为空:
check (user_id is null <> demo_user_id is null)
检查(用户id为空演示用户id为空)
当然,AR不相信FKs和检查等花哨的东西,即使它们是数据完整性所必需的。您必须手动控制demo\u user\u id
,但最好定期扫描,确保它们与Redis中的值相关联
现在,您的用户
可以使用与统计数据的标准关联来查找统计数据。用户id
列和您的演示用户
可以使用统计数据。演示用户id
我想我可能有一个解决您问题的方法,但可能不是一个更好的方法:
class User < ActiveRecord::Base
has_many :stats, primary_key: "id_s"
def id_s
read_attribute(:id).to_s
end
end
class用户
仍然使用第二个虚拟列,但可能更方便地与Rails关联一起使用,并且与数据库无关。将stats.user\u id
也作为字符串不是更有意义吗?然后是y
class User < ActiveRecord::Base
has_many :stats, primary_key: "id_s"
def id_s
read_attribute(:id).to_s
end
end