gpt4 book ai didi

sql - 使用 id 和树状表的后代连接两个表

转载 作者:搜寻专家 更新时间:2023-10-30 20:41:46 26 4
gpt4 key购买 nike

我有以下表格:

CREATE TABLE element (
element_id serial PRIMARY KEY,
local_id integer,
name varchar,
CONSTRAINT fk_element_local_id FOREIGN KEY (local_id)
REFERENCES local (local_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
);

CREATE TABLE local (
local_id serial PRIMARY KEY,
parent_id integer,
name varchar,
CONSTRAINT fk_local_parent_id_local_id FOREIGN KEY (parent_id)
REFERENCES local (local_id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE SET NULL
);

CREATE TABLE category (
category_id serial PRIMARY KEY,
name varchar
);

CREATE TABLE action (
action_id serial PRIMARY KEY,
local_id integer,
category_id integer,
CONSTRAINT fk_action_local_id FOREIGN KEY (local_id)
REFERENCES local (local_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fk_action_element_id FOREIGN KEY (element_id)
REFERENCES element (element_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
);

我想从一个 Action 中选择所有元素。如果元素的局部是 Action 局部的后代,它也应该出现。
示例:

本地:

|local_id | parent_id | name |
|---------+-----------+------|
|1 |NULL |A |
|2 |1 |B |
|3 |1 |C |
|4 |3 |D |
|5 |NULL |E |
|6 |5 |F |
|_________|___________|______|

类别:

| category_id | name |
|-------------+------|
|1 |A |
|2 |B |
|2 |C |
|_____________|______|

表格元素:

|element_id | local_id | name | category_id |
|-----------+----------+------+-------------|
|1 |1 |A | 1 |
|2 |2 |B | 2 |
|3 |2 |C | 1 |
|4 |4 |D | 2 |
|5 |5 |E | 2 |
|6 |6 |F | 1 |
|7 |6 |G | 1 |
|___________|__________|______|_____________|

Action :

|action_id | local_id | category_id |
|----------+----------+-------------|
| 1 | 1 | 2 |
| 2 | 3 | 1 |
| 3 | 5 | 1 |
| 4 | 6 | 1 |
|__________|__________|_____________|

我想要的查询结果:

CASE: action_id = 1
return: element_id: 2,4

CASE: action_id = 2
return: element_id: null

CASE: action_id = 3
return: element_id: 6,7

我已经创建了一个返回所有后代(包括实际节点)的函数,但由于调用该函数数千次时的性能,我遇到了困难。我的函数如下所示:

CREATE OR REPLACE FUNCTION fn_local_get_childs(_parent_id integer)
RETURNS SETOF integer AS
$BODY$
DECLARE
r integer;
BEGIN
FOR r IN SELECT local_id FROM local WHERE local_id IN (
(WITH RECURSIVE parent AS
(
SELECT local_id , parent_id from local WHERE local_id = _parent_id
UNION ALL
SELECT t.local_id , t.parent_id FROM parent
INNER JOIN local t ON parent.local_id = t.parent_id
)
SELECT local_id FROM parent
)
)
LOOP
RETURN NEXT r;
END LOOP;
RETURN;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;

我的超慢查询如下所示:

select e.element_id, a.action_id
from action a
join element e on (
e.local_id=any(select fn_local_get_childs(a.local_id)) AND
e.category_id=a.category_id)

有没有办法将函数中使用的递归组合到一个查询中?

最佳答案

整合查询

改进了几个地方的逻辑,您可以将整个操作集成到一个查询中。包装到 SQL 函数中是可选的:

CREATE OR REPLACE FUNCTION f_elems(_action_id integer)
RETURNS SETOF integer AS
$func$
WITH RECURSIVE l AS (
SELECT a.category_id, l.local_id
FROM action a
JOIN local l USING (local_id)
WHERE a.action_id = $1

UNION ALL
SELECT l.category_id, c.local_id
FROM l
JOIN local c ON c.parent_id = l.local_id -- c for "child"
)
SELECT e.element_id
FROM l
JOIN element e USING (category_id, local_id);
$func$ LANGUAGE sql STABLE;

检索给定 action_id 的相同元素和子局部元素的所有 element_id

调用:

SELECT * FROM f_elem(3);

element_id
-----------
6
7

db<>fiddle here
sqlfiddle

由于几个原因,这应该显着更快。最明显的是:

  • 用纯 SQL 代替 plpgsql 中的慢循环。
  • 缩小递归查询的起始集。
  • 删除不必要的和众所周知的慢 IN 结构。

我正在使用 SELECT * FROM ... 而不是 SELECT 调用,即使该行只有一个列,以获取 的列名code>OUT 参数(element_id) 我在函数头声明。

更快,但

指数

action.action_id 的索引由主键提供。

但是您可能错过了 local.parent_id 上的索引。在执行此操作时,将其设为覆盖多列索引(Postgres 9.2+),其中 parent_id 作为第一个元素,local_id 作为第二个元素。如果表 local 很大,这应该会有很大帮助。对于一张小 table 来说没有那么多或根本没有:

CREATE INDEX l_mult_idx ON local(parent_id, local_id);

为什么?见:

最后,表 元素 上的 multi-column index 应该有更多帮助:

CREATE INDEX e_mult_idx ON element (category_id, local_id, element_id);

第三列 element_id 仅用于使其成为覆盖索引。如果您的查询从表 element 中检索到更多列,您可能希望将更多列添加到索引或删除 element_id。两者都会使其更快。

物化 View

如果您的表收到很少更新或没有更新,提供所有对 (action_id, element_id) 共享相同类别的预计算集的物化 View 将使这个快如闪电。使 (action_id, element_id)(按此顺序)为主键。

关于sql - 使用 id 和树状表的后代连接两个表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16741285/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com