[英]Rolling id based on foreign key in a hierarchical schema
As an example, consider this hierarchical schema. 作为示例,请考虑此分层架构。
Assume all id fields are auto incrementing primary keys and that foreign keys are named by [parent_table_name]_id convention. 假设所有id字段都是自动递增的主键,并且外键由[parent_table_name] _id约定命名。
As soon as there are multiple companies in the database, then companies will share all primary key sequences between them. 一旦数据库中有多个公司,公司将在它们之间共享所有主键序列。
For example, if there are two company rows, the customer_group table could look like this 例如,如果有两个公司行, customer_group表可能如下所示
| id | company_id |
-------------------
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | 2 |
| 5 | 1 |
-------------------
But it should look like this 但是应该看起来像这样
| id | company_id |
-------------------
| 1 | 1 |
| 2 | 1 |
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
-------------------
This behavior should also be exhibited for customer and any other table in the tree that directly or indirectly references company . 对于客户以及树中直接或间接引用公司的任何其他表,也应显示此行为。
Note that I will most likely make a second id column (named something like relative_id ) for this purpose, keeping the unique id column intact, as this is really mostly for display purposes and how users will reference these data entities. 请注意,我很可能为此目的创建第二个id列(命名为relative_id之类的东西),保持唯一的id列不变,因为这实际上主要是出于显示目的以及用户如何引用这些数据实体。
Now if this was just one level of hierarchy, it would be a relatively simple solution. 现在,如果这只是层次结构的一个级别,那将是一个相对简单的解决方案。 I could make a table (table_name, company_id, current_id) and a trigger procedure that fires before insert on any of the tables, incrementing the current id by 1 and setting the row's relative_id to that value. 我可以制作一个表(table_name,company_id,current_id)和一个触发过程,该过程在插入任何表之前触发,将当前id递增1并将行的relative_id设置为该值。 It's trivial when the company_id is right there in the insert query. 当company_id在插入查询中在那里时,这很简单。
But how about the tables that don't reference company directly? 但是没有直接引用公司的表又如何呢? Like the lowest level of the hierarchy in this example, workorder , which only references customer . 与本示例中层次结构的最低级别一样,工作单仅引用customer 。
Is there a clean, reusable solution to climb the ladder all the way from 'customer_id' to ultimately retrieve the parenting company_id ? 是否有一个干净,可重用的解决方案,以从'customer_id'一直爬到最终检索父母的company_id ?
Going recursively up the hierarchy with SELECTs on each INSERT doesn't sound too appealing to me, performance wise. 在性能上,对每个INSERT的SELECT进行递归处理对我来说听起来不太吸引人。
I also do not like the idea of just adding a foreign key to company for each of these tables, the schema would get increasingly uglier with each additional table. 我也不喜欢只为这些表中的每个表添加外键到公司的想法,每个其他表的架构都会变得越来越难看。
But these are the two solutions I can see, but I may not be looking in the right places. 但是,这是我可以看到的两个解决方案,但我可能没有在正确的地方寻找。
The company shouldn't care what the primary key is if you're using generated keys. 如果您使用生成的密钥,公司将不在乎主密钥是什么。 They're supposed to be meaningless; 他们应该毫无意义。 compared for equality and nothing else. 比较平等,别无其他。 I grumbled about this earlier , so I'm really glad to see you write: 之前我对此事发牢骚 ,所以很高兴看到您写:
Note that I will most likely make a second id column (named something like relative_id) for this purpose, keeping the unique id column intact, as this is really mostly for display purposes and how users will reference these data entities. 请注意,我很可能为此目的创建第二个id列(命名为relative_id之类的东西),保持唯一的id列不变,因为这实际上主要是出于显示目的以及用户如何引用这些数据实体。
You're doing it right. 您做对了。
Most of the time it doesn't matter what the ID is, so you can just give them whatever comes out of a sequence and not care about holes/gaps. 在大多数情况下,ID无关紧要,因此您可以给它们提供序列中的任何内容,而不必关心孔/间隙。 If you're concerned about inter-company leakage (unlikely) you can obfuscate the IDs by using the sequence as an input to a pseudo-random generator. 如果您担心公司间泄漏(不太可能),可以通过使用序列作为伪随机生成器的输入来混淆ID。 See the function Daniel Verité wrote in response to my question about this a few years ago, pseudo_encrypt . 请参阅DanielVerité在几年前针对我对此问题的回答所编写的函数pseudo_encrypt 。
There are often specific purposes for which you need perfectly sequential gapless IDs, like invoice numbers. 通常,出于特定目的,您需要完美的顺序无间断ID,例如发票编号。 For those you need to use a counter table and - yes - look up the company ID. 对于那些您需要使用计数器表,并且-是-查找公司ID。 Such ID generation is slow and has terrible concurrency anyway, so an additional SELECT
with a JOIN
or two on indexed keys won't hurt much. 这样的ID生成速度很慢,并且具有严重的并发性,因此在索引键上使用JOIN
或两个JOIN
的附加SELECT
不会有多大伤害。 Don't go recursively up the schema with SELECT
s though, just use a series of JOIN
s. 但是,不要使用SELECT
递归地使用模式,只需使用一系列JOIN
。 For example, for an insert into workorder
your key generation trigger on workorder
would be something like the (untested): 例如,对于插入到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';
For the UPDATE ... RETURNING ... INTO
syntax see Executing a Query with a Single-Row Result . 有关UPDATE ... RETURNING ... INTO
语法的信息,请参见执行具有单行结果的查询 。
There can be gaps in normal sequences even if there's no multi-company problem. 即使没有多公司问题,正常顺序中也可能存在间隙。 Observe: 注意:
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;
Result: 结果:
regress=# SELECT * FROM demo;
id | blah
----+------
1 | aa
3 | aa
"But it should look like this" “但是看起来应该像这样”
| id | company_id |
-------------------
| 1 | 1 |
| 2 | 1 |
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
-------------------
I think it should not and I think you want a many to many relationship. 我认为不应该,而且我想您需要多对多的关系。 The customer_group table: customer_group表:
| id | name |
-------------
| 1 | n1 |
| 2 | n2 |
| 3 | n3 |
-------------
And then the customer_group_company table: 然后是customer_group_company表:
| group_id | company_id |
-------------------------
| 1 | 1 |
| 2 | 1 |
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
-------------------------
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.