我有一个我想优化的设定数值范围。
以下是初始值的简单示例:
Start End
9 12
1 2
60 88
10 11
79 80
优化后我期望输出的结果:
Start End
1 2
9 12
60 88
这些是存储在MySQL数据库中的Modified Preorder Tree Traversal(嵌套集)数据的left
和right
值。我使用它们从结果中排除非活动分支,并且目前根本没有优化范围。我认为在使用前优化范围可能会带来性能提升。
更多信息
将值传递到查询中,以使用NOT BETWEEN
子句排除树中的非活动分支。我认为我可以通过使用一组最小范围来优化该查询的性能。
答案 0 :(得分:2)
将它们放入已排序的列表中。标记排序列表中的哪些元素表示范围开始,哪些是范围结束。首先根据值对列表进行排序;但是,请确保范围在范围结束之前开始。 (这可能涉及一种可以在给定密钥上排序的某种结构。我不知道php中的细节。)
现在,从头到尾遍历列表。保留一个柜台,c
。当您通过范围开始时,请递增c
。传递范围结束时,递减c
。
当c
从0变为1时,这是最终集合中范围的开始。当c
从1变为0时,那就是范围的结束。
编辑:: 如果你已经在某个数据库表中有了范围,你可以构建一个SQL查询来执行上面的第一步(同样,确保返回范围起始点)在范围终点之前)。
答案 1 :(得分:2)
这是一个将返回你想要的SQL
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 |
+-------+------+
当然,您可以使用上述SQL创建VIEW,将数据移动到另一个表或删除行。
注意:我不确定你为什么要做这个'优化'。
编辑:
查询可以重写为
SELECT s.*
FROM sample s LEFT JOIN
sample s2 ON s.Start > s2.Start AND s.Start < s2.End
WHERE s2.start IS NULL;
这将创建不同的执行计划(2xsimple select vs EXISTS的主要/从属子查询),因此性能可能会有所不同。如果存在,两个查询都将使用(开始,结束)索引。
答案 2 :(得分:0)
这是一个简单的实现:
// 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);
限制/注释:
int
。0
,此示例将错误地从最终结果中删除任何范围。如果您的数据可以合法地包含此类范围,请提供适当的回调array_filter
:function($item) { return $item === null; }
。<强> See it in action 强>
答案 3 :(得分:0)
$ranges = array(
array(9, 12),
array(1, 2),
array(60, 81),
array(10, 11),
array(79, 88),
);
function optimizeRangeArray($r) {
$flagarr = array();
foreach ($r as $range => $bounds) {
$flagarr = array_pad($flagarr, $bounds[1], false);
for ($i = $bounds[0]-1; $i < $bounds[1]; $i++) $flagarr[$i] = true;
}
$res = array(); $min = 0; $max = 0; $laststate = false;
$ctr = 0;
foreach ($flagarr as $state) {
if ($state != $laststate) {
if ($state) $min = $ctr + 1;
else {
$max = $ctr;
$res[] = array($min, $max);
}
$laststate = $state;
}
$ctr++;
}
$max = $ctr;
$res[] = array($min, $max);
return($res);
}
print_r(optimizeRangeArray($ranges));
输出:
Array
(
[0] => Array
(
[0] => 1
[1] => 2
)
[1] => Array
(
[0] => 9
[1] => 12
)
[2] => Array
(
[0] => 60
[1] => 88
)
)
注意:这不适用于负整数!
或者像这样使用
$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