Php 数值范围优化
我有一套数值范围,我想优化 以下是初始值的简单示例:Php 数值范围优化,php,mysql,algorithm,nested-sets,modified-preorder-tree-t,Php,Mysql,Algorithm,Nested Sets,Modified Preorder Tree T,我有一套数值范围,我想优化 以下是初始值的简单示例: Start End 9 12 1 2 60 88 10 11 79 80 优化后的输出结果: Start End 1 2 9 12 60 88 这些是MySQL数据库中存储的修改后的前序树遍历嵌套集数据的左值和右值。我使用它们从结果中排除非活动分支,并且目前根本没有优化范围。我想在使用前优化范围可能会提高性能 更多信息 这些
Start End
9 12
1 2
60 88
10 11
79 80
优化后的输出结果:
Start End
1 2
9 12
60 88
这些是MySQL数据库中存储的修改后的前序树遍历嵌套集数据的左值和右值。我使用它们从结果中排除非活动分支,并且目前根本没有优化范围。我想在使用前优化范围可能会提高性能
更多信息
这些值被传递到查询中,以便使用NOT BEVER子句排除树中的非活动分支。我认为我可以通过使用一组最小的范围来优化查询的性能。将它们放在一个排序列表中。标记排序列表中哪些元素表示范围开始,哪些元素表示范围结束。首先根据值对列表进行排序;但是,请确保范围开始在范围结束之前。这可能涉及某种类型的结构,可以根据给定的键进行排序。我不知道php的细节 现在,从头到尾遍历列表。留个柜台,c。当你通过一个范围开始时,增加c。当你通过一个范围结束时,递减c 当c从0变为1时,这是最终集合中范围的开始。当c从1变为0时,这是一个范围的结束
编辑::如果数据库表中的某个位置已经有了范围,您可能可以构造一个SQL查询来再次执行上面的第一步,确保在范围结束点之前返回范围起始点。下面是一个简单的实现:
// I picked this format because it's convenient for the solution
// and because it's very natural for a human to read/write
$ranges = array(
9 => 12,
1 => 2,
60 => 81,
10 => 11,
79 => 88);
ksort($ranges);
$count = count($ranges);
$prev = null; // holds the previous start-end pair
foreach($ranges as $start => $end) {
// If this range overlaps or is adjacent to the previous one
if ($prev !== null && $start <= $prev[1] + 1) {
// Update the previous one (both in $prev and in $ranges)
// to the union of its previous value and the current range
$ranges[$prev[0]] = $prev[1] = max($end, $prev[1]);
// Mark the current range as "deleted"
$ranges[$start] = null;
continue;
}
$prev = array($start, $end);
}
// Filter all "deleted" ranges out
$ranges = array_filter($ranges);
限制/注意事项:
范围边界必须足够小,以适合整数。
如果结束边界为0,此示例将错误地从最终结果中删除任何范围。如果您的数据可以合法地包含这样一个范围,请提供对数组_filter的适当回调:函数$item{return$item==null;}。
注意:这不适用于负整数
或者像这样使用它
$rres = optimizeRangeArray($ranges);
$out = "<pre>Start End<br />";
foreach($rres as $range=>$bounds) {
$out .= str_pad($bounds[0], 9, ' ') . str_pad($bounds[1], 9, ' ') . "<br />";
}
$out .= "</pre>";
echo $out;
下面是一个SQL,它将返回您想要的内容
SELECT s.*
FROM sample s LEFT JOIN
sample s2 ON s.Start > s2.Start AND s.Start < s2.End
WHERE s2.start IS NULL;
当然,您可以使用上述SQL创建视图、将数据移动到另一个表或删除行
注意:我不太清楚你为什么要做这种“优化”
编辑:
查询可以重写为
这将为EXISTS创建不同的执行计划2xsimple select vs primary/dependent子查询,因此性能可能会有所不同。这两个查询都将在开始时使用索引,如果存在,则在结束时使用索引。因此您希望将一组范围折叠为最小范围集。您正在选择嵌套集中的顶层。这可以在SQL中完成。@Unreason-您能用一个查询示例详细说明一下吗?现在没时间了,看看它是否足以将范围从一个列表开始,范围从另一个列表结束;然后迭代它们,使用两个指针,并将指向较低数字的指针向前推进。这样,关于范围开始和结束的相对顺序的问题也就消失了,在SQL中应该更容易进行两个查询,一个查询范围开始,一个查询范围结束,然后在SQL查询中对它们进行排序。这种方法适用于查找范围并集的一般问题。您应该注意,OP特别要求来自“嵌套集”的范围,其中范围要么是正确的子集,要么没有交叉点,因此嵌套。这非常有效!我现在注意到,我有相邻的节点,我也想合并。1,2和3,4将变成1,4。@Sonny,好吧-这是一个不同的问题,对于这个问题,其他提出的解决方案都是很好的算法。然而,我有点困惑,你的目的是什么?在一般使用场景中优化嵌套集是没有必要的-它已经是一个优化的数据结构,可以利用DB索引进行递归查询。我想我会在有机会的时候创建一个新的帖子。我可能做得不对,或者我目前的解决方案可能是最优的。
$rres = optimizeRangeArray($ranges);
$out = "<pre>Start End<br />";
foreach($rres as $range=>$bounds) {
$out .= str_pad($bounds[0], 9, ' ') . str_pad($bounds[1], 9, ' ') . "<br />";
}
$out .= "</pre>";
echo $out;
Start End
1 2
9 12
60 88
mysql> CREATE TABLE sample (Start INT, End INT);
mysql> INSERT sample VALUES (9,12),(1,2),(60,88),(10,11),(79,80);
mysql> SELECT *
-> FROM sample s
-> WHERE NOT EXISTS (SELECT 1
-> FROM sample
-> WHERE s.Start > Start AND s.Start < End);
+-------+------+
| Start | End |
+-------+------+
| 9 | 12 |
| 1 | 2 |
| 60 | 88 |
+-------+------+
SELECT s.*
FROM sample s LEFT JOIN
sample s2 ON s.Start > s2.Start AND s.Start < s2.End
WHERE s2.start IS NULL;