MYSQL - 范围内快速搜索的结果之间的限制

Posted

技术标签:

【中文标题】MYSQL - 范围内快速搜索的结果之间的限制【英文标题】:MYSQL - limit between results for fast search in ranges 【发布时间】:2016-07-23 11:31:49 【问题描述】:

我在 mysql 中有两个表,其中 table2 包含 17 位数字(varchar 17)的序列号范围(唯一),table1 包含序列值(与范围相同的格式) 例如:

table 1:
serial_id   seial
1           12345678123456799

table 2:
range id      date              start                end
1             2012-01-01        12345678123456789    12345678123456999
2             2012-01-01        12345678123457000    12345678123457099
3             2012-01-01        12345678123457100    12345678123457199

我想查找每个序列所属的范围ID。可以使用的最简单的查询是:

select *
from table1,table2
where table1.serial  between table2.start and table2.end 

但我想通过以下事实优化它以更快地运行: 序列号和范围是唯一的,因此每个序列号可能属于一个且只有一个范围。因此,当一个范围包含序列时,无需搜索其他范围。 每个范围的前 11 位数字相同。例如,一个范围可以是从 12345678120000000 到 12345678129999999。 序列号和范围按日期排序,并且更有可能在早期找到范围。序列大约有 6000000 条记录,范围大约有 100000 条记录。

有更好的查询方法吗?

【问题讨论】:

你能告诉我们你从这些表中得到的真实查询吗?我问这个问题是因为您的查询返回 6m 条记录,它在现实世界中毫无用处。这将使我们能够提供更好的答案。 @Msf-vpt :我的真实查询写在上面。我已经销售了一系列具有序列号的产品批次。我的范围是已售批次,我想在已售批次中搜索使用过的批次。通过此查询,我可以找到未使用的序列号、从销售到使用的延迟、更快的卖家以及更多信息。 为所有相关表和 EXPLAIN 的结果提供 CREATE 表语句。并且不要使用邪恶的SELECT * 【参考方案1】:

这有点难以加速。这是我对 IP 地址范围使用的一种方法:

select t1.*,
       (select t2.range_id
        from table2 t2
        where t2.start <= t.serial
        order by t2.start desc
        limit 1
       ) as range_id
from table1 t1;

这可以利用table2(start, range_id) 上的索引。

注意:这不会检查范围的结尾。为此,我会添加另一个 join 。 . .虽然这(不幸)需要实现一个子查询:

select *
from (select t1.*,
             (select t2.range_id
              from table2 t2
              where t2.start <= t.serial
              order by t2.start desc
              limit 1
             ) as range_id
      from table1 t1
     ) t1 left join
     table2 t2
     on t1.range_id = t2.range_id and t2.end >= t.serial;

附加的连接需要在table2(range_id, end) 上建立索引。

【讨论】:

感谢 Gordon,但序列号可能不止一个开头,“limit 1”将选择第一个。如果第一个范围不包含序列号,则结果将不正确。例如: 5 in range (1-3,4-7,8-10) in the first and second range 5 大于 start。但第二个包含它。 @user3833757 。 . .子查询中的 order by 应该是降序的(就像现在一样)。【参考方案2】:

我认为通过数据模型的一点点改变,就会发生很大的性能提升。 通过将 rangeid 列添加到 table1 作为外键。

table 1:
serial_id   seial              rangeid
1           12345678123456799  1

然后编写以下查询:

select *
from table1 join table2 using(rangeid);

如果这种改变是不可能的,你可以使用如下的操作符:

select *
from table1 join table2
on(table2.start like concat(left(table1.serial,12),'%'))
where table1.serial  between table2.start and table2.end;

table2.start 列必须被索引。

编辑: 并根据serial字段和start字段之间的关系将数字“12”增加到最大可能的数字。

【讨论】:

谢谢,第一个无法访问,因为连续记录没有任何范围信息。第二个想法花了我更多的时间来运行。 @user3833757 ,你能说出需要多少时间吗? @user3833757 将数字“12”增加到最大可能数字。 我测试了 1000 个序列和 120000 个范围的样本,简单查询:3.109 秒。 (+ 20.766 秒网络)您的查询:3.359 秒。 (+ 20.844 秒网络) @user3833757 谢谢。您是否在 table2.start 上创建了索引?

以上是关于MYSQL - 范围内快速搜索的结果之间的限制的主要内容,如果未能解决你的问题,请参考以下文章

VS CODE中如何快速定位到需要的源码?

VS CODE中如何快速定位到需要的源码?

VS CODE中如何快速定位到需要的源码?

搜索练习

如何对foursquare场所/搜索api返回的结果进行分页?

MySQL:多个纬度和经度的附近位置