2

例如,考虑这个分层模式。 在此处输入图像描述

假设所有 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      |
-------------------

这种行为也应该展示给customer和树中直接或间接引用company的任何其他表。

请注意,我很可能会为此创建第二个id列(命名为relative_id),保持唯一id列不变,因为这实际上主要用于显示目的以及用户将如何引用这些数据实体。


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

但是那些不直接引用公司的表格呢?与本示例中的最低层次结构一样,workorder仅引用customer
是否有一个干净、可重复使用的解决方案从“customer_id”一直爬到最终检索育儿company_id

在每个 INSERT 上使用 SELECT 递归地提升层次结构对我来说听起来不太有吸引力,性能方面。

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

但这是我能看到的两个解决方案,但我可能没有在正确的地方寻找。

4

2 回答 2

1

如果您使用生成的密钥,公司不应该关心主键是什么。它们应该是没有意义的;比较平等,仅此而已。我之前对此抱怨过,所以我很高兴看到你写:

请注意,我很可能会为此创建第二个 id 列(命名为 relative_id),保持唯一 id 列不变,因为这实际上主要用于显示目的以及用户将如何引用这些数据实体。

你做对了。

大多数时候,ID 是什么并不重要,所以你可以给他们任何来自序列的东西,而不关心孔/间隙。如果您担心公司间的泄漏(不太可能),您可以使用序列作为伪随机生成器的输入来混淆 ID。请参阅 Daniel Verité 几年前针对我的问题所写的函数pseudo_encrypt

通常,您需要完全连续的无间隙 ID 用于特定目的,例如发票编号。对于那些你需要使用柜台和 - 是的 - 查找公司 ID 的人。这样的 ID 生成速度很慢,而且并发性很差,所以在索引键上增加SELECTJOIN两个键不会有太大的伤害。但是不要使用SELECTs 递归地向上架构,只需使用一系列JOINs。例如,对于插入到workorder您的密钥生成触发器,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;

结果:

regress=#     SELECT * FROM demo;
 id | blah 
----+------
  1 | aa
  3 | aa
于 2012-10-08T08:42:39.000 回答
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      |
-------------------------
于 2012-10-08T02:18:17.790 回答