https://coderwall.com/p/whf3-a/hierarchical-data-in-postgres

-------------------------------------------------------------------------------

This tip will try to answer the following questions:

  • How can we represent a tree of data in postgres
  • How can we efficiently query for any entire single node and all of it's children (and children's children).

The test data

Since we want to keep this simple we will assume our data is just a bunch of sections. A section just has a nameand each section has a single parent section.

Section A
|--- Section A.1 Section B
|--- Section B.1
|--- Section B.1
|--- Section B.1.1

We'll use this simple data for examples below.

Simple self-referencing

When designing a self-referential table (something that joins itself to itself) the most obvious choice is to have some kind of parent_id column on our table that references itself.

CREATE TABLE section (
id INTEGER PRIMARY KEY,
name TEXT,
parent_id INTEGER REFERENCES section,
);
ALTER TABLE page ADD COLUMN parent_id INTEGER REFERENCES page;
CREATE INDEX section_parent_id_idx ON section (parent_id);

Now insert our example data, using the parent_id to related the nodes together:

INSERT INTO section (id, name, parent_id) VALUES (1, 'Section A', NULL);
INSERT INTO section (id, name, parent_id) VALUES (2, 'Section A.1', 1);
INSERT INTO section (id, name, parent_id) VALUES (3, 'Section B', NULL);
INSERT INTO section (id, name, parent_id) VALUES (4, 'Section B.1', 3);
INSERT INTO section (id, name, parent_id) VALUES (5, 'Section B.2', 3);
INSERT INTO section (id, name, parent_id) VALUES (6, 'Section B.2.1', 5);

This works great for simple queries such as, fetch the direct children of Section B:

SELECT * FROM section WHERE parent = 3

but it will require complex or recursive queries for questions like fetch me all the children and children's children of Section B:

WITH RECURSIVE nodes(id,name,parent_id) AS (
SELECT s1.id, s1.name, s1.parent_id
FROM section s1 WHERE parent_id = 3
UNION
SELECT s2.id, s2.name, s2.parent_id
FROM section s2, nodes s1 WHERE s2.parent_id = s1.id
)
SELECT * FROM nodes;

So we have answered the "how to build a tree" part of the question, but are not happy with the "how to query for a node and all it's children" part.

Enter ltree. (Short for "label tree" - I think?).

The ltree extension

The ltree extension is a great choice for querying hierarchical data. This is especially true for self-referential relationships.

Lets rebuild the above example using ltree. We'll use the page's primary keys as the "labels" within our ltree paths and a special "root" label to denote the top of the tree.

CREATE EXTENSION ltree;

CREATE TABLE section (
id INTEGER PRIMARY KEY,
name TEXT,
parent_path LTREE
); CREATE INDEX section_parent_path_idx ON section USING GIST (parent_path);

We'll add in our data again, this time rather than using the id for the parent, we will construct an ltree path that represents the parent node.

INSERT INTO section (id, name, parent_path) VALUES (1, 'Section 1', 'root');
INSERT INTO section (id, name, parent_path) VALUES (2, 'Section 1.1', 'root.1');
INSERT INTO section (id, name, parent_path) VALUES (3, 'Section 2', 'root');
INSERT INTO section (id, name, parent_path) VALUES (4, 'Section 2.1', 'root.3');
INSERT INTO section (id, name, parent_path) VALUES (4, 'Section 2.2', 'root.3');
INSERT INTO section (id, name, parent_path) VALUES (5, 'Section 2.2.1', 'root.3.4');

Cool. So now we can make use of ltree's operators @> and <@ to answer our original question like:

SELECT * FROM section WHERE parent_path <@ 'root.3';

However we have introduced a few issues.

  • Our simple parent_id version ensured referential consistancy by making use of the REFERENCES constraint. We lost that by switching to ltree paths.
  • Ensuring that the ltree paths are valid can be a bit of a pain, and if paths become stale for some reason your queries may return unexpected results or you may "orphan" nodes.

The final solution

To fix these issues we want a hybrid of our original parent_id (for the referential consistency and simplicity of the child/parent relationship) and our ltree paths (for improved querying power/indexing). To achieve this we will hide the management of the ltree path behind a trigger and only ever update the parent_id column.

CREATE EXTENSION ltree;

CREATE TABLE section (
id INTEGER PRIMARY KEY,
name TEXT,
parent_id INTEGER REFERENCES section,
parent_path LTREE
); CREATE INDEX section_parent_path_idx ON section USING GIST (parent_path);
CREATE INDEX section_parent_id_idx ON section (parent_id); CREATE OR REPLACE FUNCTION update_section_parent_path() RETURNS TRIGGER AS $$
DECLARE
path ltree;
BEGIN
IF NEW.parent_id IS NULL THEN
NEW.parent_path = 'root'::ltree;
ELSEIF TG_OP = 'INSERT' OR OLD.parent_id IS NULL OR OLD.parent_id != NEW.parent_id THEN
SELECT parent_path || id::text FROM section WHERE id = NEW.parent_id INTO path;
IF path IS NULL THEN
RAISE EXCEPTION 'Invalid parent_id %', NEW.parent_id;
END IF;
NEW.parent_path = path;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql; CREATE TRIGGER parent_path_tgr
BEFORE INSERT OR UPDATE ON section
FOR EACH ROW EXECUTE PROCEDURE update_section_parent_path();

Much better.

More

Use json and plv8 to work with tree data

Written by Chris Farmiloe

Hierarchical data in postgres的更多相关文章

  1. asp.net Hierarchical Data

    Introduction A Hierarchical Data is a data that is organized in a tree-like structure and structure ...

  2. mysql 树形数据,层级数据Managing Hierarchical Data in MySQL

    原文:http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/ 引言 大多数用户都曾在数据库中处理过分层数据(hiera ...

  3. Managing Hierarchical Data in MySQL

    Managing Hierarchical Data in MySQL Introduction Most users at one time or another have dealt with h ...

  4. Managing Hierarchical Data in MySQL(邻接表模型)[转载]

    原文在:http://dev.mysql.com/tech-resources/articles/hierarchical-data.html 来源: http://www.cnblogs.com/p ...

  5. 云原生 PostgreSQL 集群 - PGO:来自 Crunchy Data 的 Postgres Operator

    使用 PGO 在 Kubernetes 上运行 Cloud Native PostgreSQL:来自 Crunchy Data 的 Postgres Operator! Cloud Native Po ...

  6. [Postgres] Group and Aggregate Data in Postgres

    How can we see a histogram of movies on IMDB with a particular rating? Or how much movies grossed at ...

  7. 《利用Python进行数据分析: Python for Data Analysis 》学习随笔

    NoteBook of <Data Analysis with Python> 3.IPython基础 Tab自动补齐 变量名 变量方法 路径 解释 ?解释, ??显示函数源码 ?搜索命名 ...

  8. Following a Select Statement Through Postgres Internals

    This is the third of a series of posts based on a presentation I did at the Barcelona Ruby Conferenc ...

  9. ZOJ 3826 Hierarchical Notation 模拟

    模拟: 语法的分析 hash一切Key建设规划,对于记录在几个地点的每个节点原始的字符串开始输出. . .. 对每一个询问沿图走就能够了. .. . Hierarchical Notation Tim ...

随机推荐

  1. web安全测试---跨站点脚本测试

    1.1      跨站脚本测试 1.1.1        GET方式跨站脚本测试 编号 SEC_Web_XSS_01 测试用例名称 GET方式跨站脚本测试 测试目的 由于跨站脚本会导致会话被劫持.敏感 ...

  2. linux随笔4

    vim编辑器: 启动vim编辑器,只需键入vim 和希望编辑的文件:vim mongo.sh 如果文件存在,将显示整个内容显示到进行编辑的缓冲区,如果文件不存在,打开一个新的缓冲区进行编辑. 内容未占 ...

  3. Django--------问题:在terminal命令行创建超级用户时入到password时输入为什么没有反应?

    首先如果遇到这样的问题不用担心,一般一会儿就可以解决: 其实,输入的时候并不是没有反应,只是你输入的时候命令行没有将你的输入显示出来,关键是输入行对Password:********也不是采用这种方式 ...

  4. 紫书第三章训练1 D - Crossword Answers

    A crossword puzzle consists of a rectangular grid of black and white squares and two lists of defini ...

  5. Timer和TimerTask详解

    1.概览 Timer是一种定时器工具,用来在一个后台线程计划执行指定任务.它可以计划执行一个任务一次或反复多次.TimerTask一个抽象类,它的子类代表一个可以被Timer计划的任务. 简单的一个例 ...

  6. BZOJ4033 [HAOI2015]树上染色 【树形dp】

    题目 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并 将其他的N-K个点染成白色.将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间 ...

  7. 【转】SpringMVC访问静态资源的三种方式

    如何你的DispatcherServlet拦截 *.do这样的URL,就不存在访问不到静态资源的问题.如果你的DispatcherServlet拦截“/”,拦截了所有的请求,同时对*.js,*.jpg ...

  8. YY的GCD(bzoj 2820)

    Description 神犇YY虐完数论后给傻×kAc出了一题给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对kAc这种 傻×必 ...

  9. CodeForces 333E. Summer Earnings

    time limit per test 9 seconds memory limit per test 256 megabytes input standard input output standa ...

  10. 【Visual Studio】让用VS2012/VS2013编写的程序在XP中顺利运行(转)

    原文转自 http://blog.csdn.net/asanscape/article/details/38752655 微软为了推销自家平台,默认配置下VS2012和VS2013编写的应用程序只能在 ...