Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails Rails有多个包含整数主键和字符串外键_Ruby On Rails_Postgresql_Foreign Keys_Nosql - Fatal编程技术网

Ruby on 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列是一个字符串,而不是一个整数 将随机字符串转换为整数不是一种

我有三个rails对象:
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