У меня есть какое-то решение."Вид", потому что мне пришлось использовать дополнительный столбец в таблице узлов: корень.Он говорит, является ли узел корневым или нет.Используя этот дополнительный бит, я могу составить такой запрос:
SELECT c.ancestor, MAX(n.time) AS t FROM closure c
JOIN nodes n ON (c.descendant = n.node AND c.ancestor <> n.node)
JOIN nodes n2 ON (c.ancestor = n2.node AND n2.root = 1)
GROUP BY c.ancestor ORDER BY t desc LIMIT 3;
Мне кажется, он работает довольно хорошо.Это тоже масштабируется.Я сгенерировал дерево с узлом 100000, и для получения результатов потребовалось около 1 секунды (максимальная глубина дерева была 18).
Я присоединяю скрипт perl (и схему таблицы) для генерации контента, так что, возможно, некоторые из них могут настроить этот запросработать лучше.
#!/usr/bin/perl --
use strict;
use warnings;
use Data::Random qw(:all);
my ($maxnode, $node) = ();
my $dbh = !DATABASE INIT!
foreach ( 1 .. $ARGV[0] ) {
$node = ($_ == 1) ? 0 : int( rand(4) );
if (!$node) {
$maxnode = &RootNode(1);
}
else {
$maxnode = &Node($maxnode);
}
}
##
##
sub Node {
my $parent = int( rand($_[0]) ) + 1;
my $id = &RootNode(0, $parent);
my $insert = qq|INSERT INTO test.closure (ancestor, descendant, depth)
SELECT ancestor, $id, depth + 1
FROM test.closure WHERE descendant = ?|;
$dbh->do($insert, undef, $parent);
return $id;
}
##
##
##
sub RootNode {
my $min_datetime = $_[0]
? '2008-9-21 4:0:0'
: $dbh->selectrow_array( "SELECT time
FROM test.nodes WHERE node = ?", undef, $_[1] );
my $label = join( "", rand_chars( set => 'alpha', min => 5, max => 20 ) );
my $time = rand_datetime( min => $min_datetime, max => 'now' );
my $insert = qq|INSERT INTO test.nodes (label, time, root) VALUES (?, ?, ?)|;
$dbh->do($insert, undef, $label, $time, $_[0]);
my ($id) = $dbh->selectrow_array("SELECT LAST_INSERT_ID()");
$insert = qq|INSERT INTO test.closure (ancestor, descendant, depth)
VALUES (?, ?, 0)|;
$dbh->do($insert, undef, $id, $id);
return $id;
}
##
__DATA__
USE test
DROP TABLE IF EXISTS `closure`;
DROP TABLE IF EXISTS `nodes`;
CREATE TABLE `nodes` (
`node` int(11) NOT NULL auto_increment,
`label` varchar(20) NOT NULL,
`time` datetime default NULL,
`root` tinyint(1) unsigned default NULL,
PRIMARY KEY (`node`)
) ENGINE=InnoDB;
CREATE TABLE `closure` (
`ancestor` int(11) NOT NULL,
`descendant` int(11) NOT NULL,
`depth` tinyint(3) unsigned default NULL,
PRIMARY KEY (`ancestor`,`descendant`),
KEY `descendant` (`descendant`),
CONSTRAINT `closure_ibfk_1` FOREIGN KEY (`ancestor`) REFERENCES `nodes` (`node`),
CONSTRAINT `closure_ibfk_2` FOREIGN KEY (`descendant`) REFERENCES `nodes` (`node`)
) ENGINE=InnoDB;