针对500万行表中最长前缀匹配的最佳MYSQL查询

针对500万行表中最长前缀匹配的最佳MYSQL查询,mysql,sql,Mysql,Sql,我有一张500万行的桌子 CREATE TABLE dummy_table ( num VARCHAR(16) NOT NULL DEFAULT 0, rsid VARCHAR(16) NOT NULL, list VARCHAR(128) NOT NULL, PRIMARY KEY (num, rsid) ); 表中的num字段是某个动态数字的前缀('123457467

我有一张500万行的桌子

CREATE TABLE dummy_table (
            num VARCHAR(16) NOT NULL DEFAULT 0,
            rsid VARCHAR(16) NOT NULL,
            list VARCHAR(128) NOT NULL,
            PRIMARY KEY (num, rsid)
            );  
表中的
num
字段是某个动态数字的前缀('123457467890'在上面的查询中)。
现在我需要根据
num
rsid
获取
列表
列,并且在此
num
中必须是传入数字的最长前缀匹配。对于获取
列表
,我有一个以下查询:

select list 
from dummy_table 
where '123457467890' like CONCAT(num, '%') 
  and rsid = '123' 
order by LENGTH(num) desc LIMIT 1;
注意:123457467890`:每次我们抛出一个查询时,这个数字都会不同


现在的问题是执行这个查询,MYSQL大约需要0.80秒,这在我的例子中非常高。我需要在一秒钟内抛出1000多个查询。是否有任何方法可以将此查询优化到这种程度。有人能帮我实现这个结果吗?

我的第一个优化是:
-添加另一列“长度”
-在
上添加索引(rsid,length DESC,num)

现在,您的查询只是略有不同:
-
从dummy_表中选择列表,其中'123457467890'像CONCAT(num,“%”)和rsid='123'按长度顺序描述限制1

但是,通过在索引中包含长度,查询应该能够在第一次命中时停止

但是…

这将永远是一个代价高昂的过程。最糟糕的情况是,您找不到匹配项,因此无论发生什么情况,都必须扫描完整的(rsid='123')记录集

上面的优化并不能帮助优化最坏的情况,只能优化最佳情况。(比赛时间越长,帮助越大,但对较短的比赛没有多大帮助。)


你可能被迫做的事情是……
1.创建临时表
2.在其中插入“1234567890”
3.在其中插入“123456789”
4.在其中插入“12345678”
.
.
.
N在其中插入“1”

此时,您在临时表中找到了搜索字符串的所有可能匹配项

然后,您的查询可能会使用索引查找。可能会找到10个匹配项(在本例中),然后找到其中最长的匹配项

-- Index now needs to be (rsid, num, length)

SELECT
  *
FROM
  dummy_table
INNER JOIN
  your_search_table
    ON dummy_table.num = your_search_table.num
WHERE
  rsid = '123'
ORDER BY
  dummy_table.length
LIMIT
  1

像CONCAT(num,“%”)这样的测试
'123457467890'
无法使用索引进行优化。但是,这相当于:

num IN ('1', '12', '123', '1234', '12345', '123456', '1234567', '12345678', '123456789', '1234567890')
如果在
num
列上有索引,则可以对其进行优化

如果您是从编程语言生成查询,那么将其转换为这种格式应该相对简单。例如,在PHP中,它将是:

$nums = array();
for ($i = 1; $i <= strlen($number); $i++) {
    $nums[] = "'" . substr($number, 0, $i) . "'";
}
$where = 'num IN (' . implode(', ', $nums) . ')'
$nums=array();

对于($i=1;$i请注意,使用适当的索引,固定长度匹配查询将运行得非常快

-- 10 number match
select list from dummy_table 
where '123457467890' = num and rsid = '123';

-- 9 number match
select list 
from dummy_table 
where '12345746789' = num and rsid = '123';
使用代码换行,以传入字符串的长度开始,并在找到匹配项时停止。即使对于完整的16长度字符串,也最多会有16个查询。虽然它执行多个查询,但它可以避免计算任何数据库字段、进行比最长匹配项短的任何比较或创建中间结果集


现在要每秒执行1000个查询,这取决于它们是独立的还是可以作为一个批处理运行,方法是加载一个额外的表,其中包含您想要的结果,并根据上述查询运行16条UPDATE语句,以填充结果。

这可能只是一种普遍的麻木感,在每年的这个时候,这种麻木感会下降,但我不能很清楚你在做什么。考虑提供适当的DDL(和/或SqLFIDLE)。以及所需的结果集。我同意您添加另一列长度的第一种方法。但是在您的第二个建议中,正如我已经提到的,这个数字“123457467890”将是一个动态数字,即每个查询可能不同,因此创建临时表将不可行。您还有其他建议吗否认这一事实?没有理由不创建临时表。创建它,编写一个循环,用所有可能的较短值填充它,在一个会话中执行查询。作为临时表,没有并发问题。