我有一个获取项目列表的SQL查询。问题是项目可以链接到另一个项目(因为它共享一些资源)。我想获得一组项目,这样就不会在各组之间共享资源。
我确信这是一个常见的图形问题,但我不知道如何处理它。解决问题的最佳方法是什么?是否有算法在SQL查询中解决它?
(编辑)尝试使用分层方法对我拥有的数据来说很慢,我想因为它没有按层次结构组织。它可以进行预处理,使其更具层次性:
(1) (1) (1)
/|\ => | => |
/ | \ (3) .. (4) (3) .. (4)
(3)..|..(4) \ |
(5) (5) (5)
我看到了一种方法,但不知道何时停止该过程,除了计算每次迭代中的所有更改,并在没有可能的更改时停止。这是一种递归方式吗?
答案 0 :(得分:1)
解决它的一种方法是使用分层查询:
Oracle 11g R2架构设置:
CREATE TABLE projects ( project ) AS
SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 10;
CREATE TABLE project_links ( project, link ) AS
SELECT 1, 2 FROM DUAL UNION ALL
SELECT 2, 4 FROM DUAL UNION ALL
SELECT 4, 5 FROM DUAL UNION ALL
SELECT 3, 5 FROM DUAL UNION ALL
SELECT 8, 6 FROM DUAL UNION ALL
SELECT 8, 7 FROM DUAL UNION ALL
SELECT 9, 6 FROM DUAL;
查询1 :
WITH two_way_links AS (
SELECT project, link
FROM project_links
UNION ALL
SELECT link, project
FROM project_links
GROUP BY project, link
)
SELECT MIN( CONNECT_BY_ROOT( p.project ) ) As root,
COALESCE( l.link, p.project ) AS projects
FROM projects p
LEFT OUTER JOIN two_way_links l
ON ( p.project = l.project )
CONNECT BY NOCYCLE
PRIOR l.link = p.project
GROUP BY COALESCE( l.link, p.project )
ORDER BY 1, 2
<强> Results 强>:
| ROOT | PROJECTS |
|------|----------|
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 1 | 5 |
| 6 | 6 |
| 6 | 7 |
| 6 | 8 |
| 6 | 9 |
| 10 | 10 |
<强>更新强>:
您可以尝试查找最小组值来尝试加快速度:
(Explanation here in a related question)
Oracle 11g R2架构设置:
CREATE TABLE projects ( project ) AS
SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 10;
CREATE TABLE project_links ( project, link ) AS
SELECT 1, 2 FROM DUAL UNION ALL
SELECT 2, 4 FROM DUAL UNION ALL
SELECT 4, 5 FROM DUAL UNION ALL
SELECT 3, 5 FROM DUAL UNION ALL
SELECT 8, 6 FROM DUAL UNION ALL
SELECT 8, 7 FROM DUAL UNION ALL
SELECT 9, 6 FROM DUAL;
查询1 :
WITH two_way_links AS (
SELECT project, link
FROM project_links
UNION ALL
SELECT link, project
FROM project_links
GROUP BY project, link
),
min_links AS (
SELECT l.*,
FIRST_VALUE( LEAST( project, link ) )
OVER( PARTITION BY project ORDER BY link ) AS min_link
FROM two_way_links l
)
SELECT MIN( CONNECT_BY_ROOT( p.project ) ) As root,
COALESCE( l.link, p.project ) AS projects
FROM projects p
LEFT OUTER JOIN min_links l
ON ( p.project = l.project )
START WITH p.project = l.min_link OR l.min_link IS NULL
CONNECT BY NOCYCLE
PRIOR l.link = p.project
GROUP BY COALESCE( l.link, p.project )
ORDER BY 1, 2
<强> Results 强>:
| ROOT | PROJECTS |
|------|----------|
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 1 | 5 |
| 6 | 6 |
| 6 | 7 |
| 6 | 8 |
| 6 | 9 |
| 10 | 10 |
答案 1 :(得分:0)
基于执行深度优先搜索的PL / SQL解决方案:
Oracle 11g R2架构设置:
CREATE TABLE projects ( project ) AS
SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 10
/
CREATE TABLE project_links ( project, link ) AS
SELECT 1, 2 FROM DUAL UNION ALL
SELECT 2, 4 FROM DUAL UNION ALL
SELECT 4, 5 FROM DUAL UNION ALL
SELECT 3, 5 FROM DUAL UNION ALL
SELECT 8, 6 FROM DUAL UNION ALL
SELECT 8, 7 FROM DUAL UNION ALL
SELECT 9, 6 FROM DUAL
/
CREATE TYPE IntList IS TABLE OF INTEGER
/
CREATE TYPE project_group IS OBJECT(
project INTEGER,
project_group INTEGER
)
/
CREATE TYPE project_group_tab IS TABLE OF PROJECT_GROUP
/
CREATE PACKAGE projects_pkg IS
TYPE project_vertex
IS RECORD(
project PROJECTS.PROJECT%TYPE,
project_group INTEGER,
links IntList
);
TYPE project_vertex_assoc_array
IS TABLE OF project_vertex
INDEX BY PLS_INTEGER;
FUNCTION get_project_groups RETURN project_group_tab PIPELINED;
END;
/
CREATE PACKAGE BODY projects_pkg IS
FUNCTION get_project_groups RETURN project_group_tab PIPELINED
IS
p_projects project_vertex_assoc_array;
i PLS_INTEGER;
j PLS_INTEGER;
k PLS_INTEGER;
g PLS_INTEGER := 0;
stack IntList;
BEGIN
FOR rec IN ( SELECT project FROM projects )
LOOP
p_projects(rec.project).project := rec.project;
p_projects(rec.project).links := IntList();
END LOOP;
FOR rec IN ( SELECT project, link FROM project_links )
LOOP
p_projects(rec.project).links.EXTEND;
p_projects(rec.project).links(p_projects(rec.project).links.COUNT) := rec.link;
p_projects(rec.link).links.EXTEND;
p_projects(rec.link).links(p_projects(rec.link).links.COUNT) := rec.project;
END LOOP;
i := p_projects.FIRST;
WHILE i IS NOT NULL LOOP
IF p_projects(i).project_group IS NULL THEN
g := g + 1;
stack := IntList( i );
p_projects(i).project_group := g;
WHILE stack.COUNT > 0 LOOP
j := stack(stack.COUNT);
stack.TRIM;
FOR n IN REVERSE 1 .. p_projects(j).links.COUNT LOOP
k := p_projects(j).links(n);
IF p_projects(k).project_group IS NULL THEN
p_projects(k).project_group := g;
stack.EXTEND;
stack( stack.COUNT ) := k;
END IF;
END LOOP;
END LOOP;
END IF;
PIPE ROW(
project_group(
p_projects(i).project,
p_projects(i).project_group
)
);
i := p_projects.NEXT(i);
END LOOP;
END;
END;
/
查询1 :
SELECT *
FROM TABLE( projects_pkg.get_project_groups )
<强> Results 强>:
| PROJECT | PROJECT_GROUP |
|---------|---------------|
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 1 |
| 6 | 2 |
| 7 | 2 |
| 8 | 2 |
| 9 | 2 |
| 10 | 3 |