如何有效地将树状数据结构插入 postgres
Posted
技术标签:
【中文标题】如何有效地将树状数据结构插入 postgres【英文标题】:How to efficiently insert tree-like data structure into postgres 【发布时间】:2021-08-31 18:33:07 【问题描述】:本质上,我想使用 Postgres 有效地将树状数据结构存储在表中。每行都有一个 ID(插入时自动生成)、一个父 ID(引用同一个表中的另一行,可能为 null)和一些额外的元数据。所有这些数据都是一次进入的,所以我试图尽可能高效地一次存储所有数据。
我目前的想法是按它们所在的树的哪个级别对所有数据进行分组,并一次批量插入一个级别。这样我就可以使用从上一级插入中生成的 ID 来设置父 ID。这样,批次的数量与树中的层数相关。
这可能“足够好”,但我想知道是否有更好的方法来做这种事情?当我的整个数据树已经在内存中并且结构正确时,这对我来说似乎仍然有很多来回和不必要的逻辑。
【问题讨论】:
您在输入数据中必然会有一些关于谁向谁报告的信息,否则您将无法按照您的意愿插入数据。可以分享一下吗? @marcothesane 当然,所以我使用的是 Java,我基本上有一个List<Foo>
,其中每个 Foo
对象都有一个其子 Foo
对象的列表,每个子对象都有一个列表它的孩子,等等。所需的 ID 是在插入 postgres 时自动生成的,所以我事先并不知道它们。
【参考方案1】:
也许对于您的用例,您现在可以尝试使用 NoSql,查询此类数据会更加高效和快捷。不妨试一试。
对于开发,您有 Apache CouchDB、redis 等选项。
【讨论】:
这绝对是理想的解决方案,是的。不幸的是,迁移所有现有数据将是一项非常重大的工作,所以我目前只是在为 postgres 寻找一个权宜之计的解决方案。感谢您的建议!【参考方案2】:如果我有一些关于谁是谁的孩子记录的信息,让我展示一下我会怎么做。
就我而言,我使用一个包含来自源的信息的临时表。这些记录有一个基于字符的主键id
,和一个自引用、可为空的外键boss_id
。
这里是:
-- the input table with "business identifiers".
DROP TABLE IF EXISTS rec_input;
CREATE TABLE rec_input (
id CHAR(4)
, first_name VARCHAR(32)
, last_name VARCHAR(32)
, boss_id CHAR(4)
)
;
-- some data for it ...
INSERT INTO rec_input(id,first_name,last_name,boss_id)
SELECT 'A01','Arthur','Dent' ,NULL
UNION ALL SELECT 'A02','Ford','Prefect' ,'A01'
UNION ALL SELECT 'A03','Zaphod','Beeblebrox' ,'A01'
UNION ALL SELECT 'A04','Tricia','McMillan' ,'A01'
UNION ALL SELECT 'A05','Gag','Halfrunt' ,'A02'
UNION ALL SELECT 'A06','Prostetnic Vogon','Jeltz','A02'
UNION ALL SELECT 'A07','Lionel','Prosser' ,'A04'
UNION ALL SELECT 'A08','Benji','Mouse' ,'A04'
UNION ALL SELECT 'A09','Frankie','Mouse' ,'A04'
UNION ALL SELECT 'A10','Svlad','Cjelli' ,'A03'
;
-- create a lookup table. The surrogate key is created here.
DROP TABLE IF EXISTS lookup_help;
CREATE TABLE lookup_help (
sk SERIAL NOT NULL -- < here is the surrogate auto increment key
, id CHAR(3)
);
-- fill the lookup table
INSERT INTO lookup_help(id)
SELECT id FROM rec_input;
-- test query
SELECT * FROM lookup_help;
-- this is the target table, with auto increment
-- and matching surrogate foreign key.
DROP TABLE IF EXISTS rec;
CREATE TABLE rec (
sk INTEGER NOT NULL -- surrogate key
, id CHAR(4). -- "business id"
, first_name VARCHAR(32)
, last_name VARCHAR(32)
, boss_id CHAR(4). -- "business foreign key", not needed really
, boss_sk INTEGER. -- internal foreign key
)
;
INSERT INTO rec
SELECT
l.sk -- from lookup table, inner joined
, i.id -- from input table
, i.first_name
, i.last_name
, i.boss_id
, b.sk -- from lookup table, left outer joined
FROM rec_input i
JOIN lookup_help l USING(id) -- for the main sk
LEFT JOIN lookup_help b ON i.boss_id=b.id -- for the foreign sk
;
-- test query
SELECT * FROM rec;
-- out sk | id | first_name | last_name | boss_id | boss_sk
-- out ----+------+------------------+------------+---------+---------
-- out 2 | A02 | Ford | Prefect | A01 | 1
-- out 3 | A03 | Zaphod | Beeblebrox | A01 | 1
-- out 4 | A04 | Tricia | McMillan | A01 | 1
-- out 6 | A06 | Prostetnic Vogon | Jeltz | A02 | 2
-- out 5 | A05 | Gag | Halfrunt | A02 | 2
-- out 10 | A10 | Svlad | Cjelli | A03 | 3
-- out 7 | A07 | Lionel | Prosser | A04 | 4
-- out 8 | A08 | Benji | Mouse | A04 | 4
-- out 9 | A09 | Frankie | Mouse | A04 | 4
-- out 1 | A01 | Arthur | Dent | |
-- out (10 rows)
【讨论】:
嗯,这绝对给了我一些思考,谢谢。我认为这样做的困难在于,出于我的目的,“业务 ID”与实际 ID 之间的关联必须在数据库之外进行 - 并将我进入的数据量插入两个单独的表(查找表和实际表) ) 可能有点问题。 创建一个临时表并在代码中的“元数据”中维护自己的标识符以放入临时表中,然后继续我的做法。不需要时,您可以随时删除暂存表和查找表。以上是关于如何有效地将树状数据结构插入 postgres的主要内容,如果未能解决你的问题,请参考以下文章