在分层模式中基于外键滚动id

时间:2012-10-08 01:56:50

标签: sql postgresql database-design

作为示例,请考虑此分层架构。 enter image description here

假设所有id字段都是自动递增主键,并且外键由 [parent_table_name] _id 约定命名。

问题

只要数据库中有多家公司,公司就会共享它们之间的所有主键序列。

例如,如果有两个公司行, customer_group 表可能如下所示

| id | company_id |
-------------------
| 1  |     1      |
| 2  |     1      |
| 3  |     2      |
| 4  |     2      |
| 5  |     1      |
-------------------

但它看起来应该是这样的

| id | company_id |
-------------------
| 1  |     1      |
| 2  |     1      |
| 1  |     2      |
| 2  |     2      |
| 3  |     1      |
-------------------

客户以及树中直接或间接引用公司的任何其他表格也应展示此行为。

请注意,为此,我很可能会创建第二个 id 列(名称类似 relative_id ),并保留唯一的 id 列完整,因为这主要用于显示目的以及用户将如何引用这些数据实体。


现在,如果这只是一个层次结构,那么这将是一个相对简单的解决方案。 我可以创建一个表(table_name,company_id,current_id)和一个在插入任何表之前触发的触发器过程,将当前id递增1并将行的 relative_id 设置为该值。 当插入查询中 company_id 就在那里时,这是微不足道的。

但是那些没有直接引用公司的表怎么样? 与此示例中最低级别的层次结构一样,工作订单,仅引用客户
是否有一个干净,可重复使用的解决方案,从'customer_id'一直爬到梯子上,最终找回育儿 company_id

在每个INSERT上以SELECTs递归递增层次结构对我来说听起来不太吸引人,性能明智。

我也不喜欢只为这些表中的公司添加外键的想法,每个附加表的架构会变得越来越丑。

但这些是我能看到的两种解决方案,但我可能没有找到合适的位置。

2 个答案:

答案 0 :(得分:1)

如果您使用生成的密钥,公司不应该关心主键是什么。他们应该毫无意义;比较平等而不是别的。我grumbled about this earlier,所以我很高兴看到你写道:

  

请注意,我很可能会创建第二个id列(命名为   像relative_id)为此目的,保持唯一的id列   完整,因为这主要是为了显示目的和如何用户   将引用这些数据实体。

你做对了。

大多数时候ID都不重要,所以你可以给它们一些序列中的任何东西,而不关心孔/间隙。如果您担心公司间泄漏(不太可能),您可以使用序列作为伪随机生成器的输入来混淆ID。请参阅DanielVerité几年前回答我关于此问题的函数pseudo_encrypt

通常有特定的目的,您需要完美的顺序无间隙ID,如发票号码。对于那些你需要使用计数器表的人 - 是的 - 查找公司ID。无论如何,这样的ID生成很慢并且具有可怕的并发性,因此在索引键上具有SELECT或两个JOIN的额外SELECT不会造成太大损害。不要使用JOIN递归递增模式,只需使用一系列workorder s。例如,对于插入workorder CREATE OR REPLACE FUNCTION workorder_id_tgfn() RETURNS trigger AS $$ BEGIN IF tg_op = 'INSERT' THEN -- Get a new ID, locking the row so no other transaction can add a -- workorder until this one commits or rolls back. UPDATE workorder_ids SET next_workorder_id = next_workorder_id + 1 WHERE company_id = (SELECT company_id FROM customer INNER JOIN customer_group ON (customer.customer_group_id = customer_group.id) INNER JOIN company ON (customer_group.company_id = company.id) WHERE customer.id = NEW.customer_id) RETURNING next_workorder_id INTO NEW.id; END IF; END; $$ LANGUAGE 'plpgsql'; 上的密钥生成触发器将类似于(未经测试):

UPDATE ... RETURNING ... INTO

对于CREATE TABLE demo (id serial primary key, blah text); BEGIN; INSERT INTO demo(blah) values ('aa'); COMMIT; BEGIN; INSERT INTO demo(blah) values ('bb'); ROLLBACK; BEGIN; INSERT INTO demo(blah) values ('aa'); COMMIT; SELECT * FROM demo; 语法,请参阅Executing a Query with a Single-Row Result

即使没有多公司问题,正常序列也可能存在差距。观察:

regress=#     SELECT * FROM demo;
 id | blah 
----+------
  1 | aa
  3 | aa

结果:

{{1}}

答案 1 :(得分:0)

“但它应该看起来像这样”

| id | company_id |
-------------------
| 1  |     1      |
| 2  |     1      |
| 1  |     2      |
| 2  |     2      |
| 3  |     1      |
-------------------

我认为不应该,我认为你想要多对多的关系。 customer_group表:

| id | name |
-------------
| 1  |  n1  |
| 2  |  n2  |
| 3  |  n3  |
-------------

然后是customer_group_company表:

| group_id | company_id |
-------------------------
|    1     |     1      |
|    2     |     1      |
|    1     |     2      |
|    2     |     2      |
|    3     |     1      |
-------------------------