在@NealB解决方案之后编辑:@ NealB的解决方案非常快速地与any another one进行比较,并且分发了关于“添加约束以提高性能”的新问题。 @ NealB不需要任何改进, O(n)时间非常简单。
“label transitive groups with SQL”的问题有an elegant solution using recursion and CTE ......但是此解决方案会消耗指数时间(!)。我需要使用10000个itens:1000个itens需要1秒钟,2000个需要1天......
约束:在我的情况下可以将问题分解为约100个或更少的部分,但只能选择一组~10个itens,并丢弃所有其他~90个标记为itens ...
有一个通用的algotithm来添加和使用这种“预选”,以减少二次, O(N ^ 2),时间?也许,如评论和@wildplasser所示, O(N log(N))时间;但我希望,“预选”可以减少到 O(N)时间。
(编辑)
我尝试使用alternative algorithm,但需要一些改进才能在此处作为解决方案使用;或者,为了真正提高性能( O(N)时间),需要使用“预选”。
“预选”(约束)基于“超集分组”......由原"How to label 'transitive groups' with SQL?" question t1
表说明,
table T1
(original T1 augmented by "super-set grouping label" ssg, and more one row)
ID1 | ID2 | ssg
1 | 2 | 1
1 | 5 | 1
4 | 7 | 1
7 | 8 | 1
9 | 1 | 1
10 | 11 | 2
所以有三组,
g1
:{1,2,5,9}因为“1 t 2”,“1 t 5”和“9 t 1“g2
:{4,7,8}因为“4 t 7”和“7 t 8”g3
:{10,11}因为“10 t 11”超级组只是辅助分组,
ssg1
:{g1,g2} ssg2
:{g3} 如果我们有 M 超级组项和 N 总计T1
项,则平均组长度将小于N / M.我们可以假设(对于我的典型问题)ssg
最大长度是~N / M.
因此, the "label algorithm"如果使用ssg
约束,则需要使用~N / M项运行M次。
答案 0 :(得分:2)
这里只有一个SQL问题似乎有点问题。在一些程序的帮助下 在SQL之上编程,解决方案似乎是简单而有效的失败。这是一个简要的概述 可以使用任何调用SQL的过程语言实现的解决方案。
使用主键R
声明表ID
,其中ID
对应与表ID1
的{{1}}和ID2
相同的域。
表格T1
包含另一个非关键列,R
数字
使用Label
中找到的值范围填充表格R
。将T1
设置为零(无标签)。
使用示例数据,Label
的初始设置如下所示:
R
使用主机语言光标和辅助计数器,从Table R
ID Label
== =====
1 0
2 0
4 0
5 0
7 0
8 0
9 0
读取每一行。在T1
中查找ID1
和ID2
。你会发现其中一个
四个案例:
R
在这种情况下,以前都没有“看到”这些 Case 1: ID1.Label == 0 and ID2.Label == 0
中的任何一个:向计数器添加1然后更新两个
ID
行到计数器的值:R
update R set R.Label = :counter where R.ID in (:ID1, :ID2)
在这种情况下, Case 2: ID1.Label == 0 and ID2.Label <> 0
是新的,但已为ID1
分配了标签。 ID2
需要分配到。{1}}
与ID1
相同的标签:ID2
update R set R.Lablel = :ID2.Label where R.ID = :ID1
在这种情况下, Case 3: ID1.Label <> 0 and ID2.Label == 0
是新的,但已为ID2
分配了标签。 ID1
需要分配到。{1}}
与ID2
相同的标签:ID1
update R set R.Lablel = :ID1.Label where R.ID = :ID2
在这种情况下,该行包含冗余信息。 Case 4: ID1.Label <> 0 and ID2.Label <> 0
的两行都应包含相同的Label值。如果不,
存在某种数据完整性问题。啊......不太看见编辑...
编辑我刚刚意识到,这里的R
值都可能非零且不同。如果两者都非零并且不同,则此时需要合并两个Label
组。您只需选择一个Label
并更新其他内容即可与Label
匹配。现在,这两个组已合并为相同的update R set R.Label to ID1.Label where R.Label = ID2.Label
值。
完成游标后,表Label
将包含更新R
所需的标签值。
T2
流程表Table R
ID Label
== =====
1 1
2 1
4 2
5 1
7 2
8 2
9 1
使用类似T2
的内容。最终结果应该是:
set T2.Label to R.Label where T2.ID1 = R.ID
这个过程是极其迭代的,应该毫不费力地扩展到相当大的表。
答案 1 :(得分:1)
我建议你检查一下并使用一些
解决它的通用语言。
http://en.wikipedia.org/wiki/Disjoint-set_data_structure
遍历图形,可以从每个节点运行DFS或BFS,
然后使用这个不相交的集合提示。我认为这应该有用。
答案 2 :(得分:0)
@NealB解决方案更快(!)见example of PostgreSQL implementation here。
下面是另一个“暴力算法”的例子,仅用于好奇心!
正如@ peter.petrov和@RBarryYoung所建议的那样,可以避免一些性能问题放弃CTE递归...我do some issues at the basic labeler,并且,abover我添加了一个超集标签分组的约束。这个新的transgroup1_loop()
函数正在运行!
PS:此解决方案仍有性能限制,请更好地发布您的答案,或者对此进行一些改编。
-- DROP table transgroup1;
CREATE TABLE transgroup1 (
id serial NOT NULL PRIMARY KEY,
items integer[], -- two or more items in the transitive relationship
ssg_label varchar(12), -- the super-set gropuping label
dels integer[] DEFAULT array[]::integer[]
);
INSERT INTO transgroup1(items,ssg_label) values
(array[1, 2],'1'),
(array[1, 5],'1'),
(array[4, 7],'1'),
(array[7, 8],'1'),
(array[9, 1],'1'),
(array[10, 11],'2');
-- or SELECT array[id1, id2],ssg_label FROM t1, with 10000 items
他们,通过这两个功能,我们可以解决问题,
CREATE FUNCTION transgroup1_loop(p_ssg varchar, p_max_i integer DEFAULT 100)
RETURNS integer AS $funcBody$
DECLARE
cp_dels integer[];
i integer;
BEGIN
i:=1;
LOOP
UPDATE transgroup1
SET items = array_uunion(transgroup1.items,t2.items),
dels = transgroup1.dels || t2.id
FROM transgroup1 AS t1, transgroup1 AS t2
WHERE transgroup1.id=t1.id AND t1.ssg_label=$1 AND
t1.id>t2.id AND t1.items && t2.items;
cp_dels := array(
SELECT DISTINCT unnest(dels) FROM transgroup1
); -- ensures all itens to del
RAISE NOTICE '-- bug, repeting dels, item-%; % dels! %', i, array_length(cp_dels,1), array_to_string(cp_dels,';','*');
EXIT WHEN i>p_max_i OR array_length(cp_dels,1)=0;
DELETE FROM transgroup1
WHERE ssg_label=$1 AND id IN (SELECT unnest(cp_dels));
UPDATE transgroup1 SET dels=array[]::integer[];
i:=i+1;
END LOOP;
UPDATE transgroup1 -- only to beautify
SET items = ARRAY(SELECT unnest(items) ORDER BY 1 desc);
RETURN i;
END;
$funcBody$ LANGUAGE plpgsql VOLATILE;
运行并查看结果,可以使用
SELECT transgroup1_loop('1'); -- run with ssg-1 items only
SELECT transgroup1_loop('2'); -- run with ssg-2 items only
-- show all with a sequential group label:
SELECT *, dense_rank() over (ORDER BY id) AS group_label from transgroup1;
结果:
id | items | ssg_label | dels | group_label
----+-----------+-----------+------+-------------
4 | {8,7,4} | 1 | {} | 1
5 | {9,5,2,1} | 1 | {} | 2
6 | {11,10} | 2 | {} | 3
PS:函数array_uunion()
是same as original,
CREATE FUNCTION array_uunion(anyarray,anyarray) RETURNS anyarray AS $$
-- ensures distinct items of a concatemation
SELECT ARRAY(SELECT unnest($1) UNION SELECT unnest($2))
$$ LANGUAGE sql immutable;