如果您对前缀范围内的重叠数量做出一个温和的假设,则可以使用 MongoDB 或 MySQL 以最佳方式执行您想要的操作。在下面的答案中,我将使用 MongoDB 进行说明,但是将这个答案移植到 MySQL 应该很容易。
首先,让我们重新表述一下这个问题。当您谈论匹配“前缀范围”时,我相信您实际上在谈论的是在字典顺序下找到正确的范围(直观地说,这只是字符串的自然字母顺序)。例如,前缀匹配 54661601 到 54661679 的数字集恰好是当写为字符串时,在字典上大于或等于“54661601”但在字典上小于“54661680”的数字集。因此,您应该做的第一件事是将所有上限加 1 ,这样您就可以用这种方式表达您的查询。在 mongo 中,您的文档看起来像
{low: "54661601", high: "54661680", bin: "a"}
{low: "526219100", high: "526219200", bin: "b"}
{low: "4305870404", high: "4305870405", bin: "c"}
现在问题变成了:给定一组 [ low , high )形式的一维区间,我们如何快速找到哪些区间包含给定点?最简单的方法是在低或高字段上使用索引。让我们使用高场。在 mongo 外壳中:
db.coll.ensureIndex({high : 1})
现在,让我们假设间隔根本不重叠。如果是这种情况,那么对于给定的查询点“x”,包含“x”的唯一可能区间是具有大于“x”的最小高值的区间。因此我们可以查询该文档并检查其低值是否也小于“x”。例如,这将打印出匹配间隔,如果有的话:
db.coll.find({high : {'$gt' : "5466160179125211"}}).sort({high : 1}).limit(1).forEach(
function(doc){ if (doc.low <= "5466160179125211") printjson(doc) }
)
现在假设不是假设间隔根本不重叠,而是假设每个间隔与小于k个相邻间隔重叠(我不知道k的值对你来说是否正确,但希望它很小)。在这种情况下,您可以在上面的“限制”中将 1 替换为k,即
db.coll.find({high : {'$gt' : "5466160179125211"}}).sort({high : 1}).limit(k).forEach(
function(doc){ if (doc.low <= "5466160179125211") printjson(doc) }
)
这个算法的运行时间是多少?索引是使用 B-trees 存储的,因此如果您的数据集中有n 个区间,则需要 O(log n ) 时间来查找第一个匹配的高值文档,然后需要 O( k ) 时间来迭代下一个k文档,总共 O(log n + k ) 时间。如果k是常数,或者实际上小于 O(log n ),那么这是渐近最优的(这是在标准计算模型中;我没有计算外部存储器传输的数量或任何花哨的东西)。
唯一会发生故障的情况是k很大时,例如,如果某个大区间几乎包含所有其他区间。在这种情况下,运行时间为 O( n )。如果您的数据是这样的结构,那么您可能需要使用不同的方法。一种方法是使用 mongo 的“2d”索引,您的低值和高值编码x和y坐标。然后,您的查询将对应于查询x - y平面的给定区域中的点。这在实践中可能会做得很好,尽管使用当前的 2d 索引实现,最坏的情况仍然是 O(n)。
对于所有k值,有许多理论结果可以实现 O(log n ) 性能。它们的名称包括优先级搜索树、段树、间隔树等。但是,这些是您必须自己实现的专用数据结构。据我所知,目前没有流行的数据库实现它们。