Mysql 优化慢速查询以查找序列中的漏洞
我有一张这样的桌子:Mysql 优化慢速查询以查找序列中的漏洞,mysql,sql,Mysql,Sql,我有一张这样的桌子: id | serial_number_basic | product_id ------------------------------------- 序列号\u basic是一个连续的号码,每次需要新号码时,它都会计数。在过去,可以将此数字的整个范围留空,而下一个数字是MAX()+1 由于要求发生变化,应立即填充空孔。序列号当然取决于产品id。每个产品都有自己的序列号基本。问题是找到漏洞 此查询确实会根据每个[productid]查找漏洞,但不幸的是,它太慢了: SEL
id | serial_number_basic | product_id
-------------------------------------
序列号\u basic是一个连续的号码,每次需要新号码时,它都会计数。在过去,可以将此数字的整个范围留空,而下一个数字是MAX()+1
由于要求发生变化,应立即填充空孔。序列号当然取决于产品id。每个产品都有自己的序列号基本。问题是找到漏洞
此查询确实会根据每个[productid]查找漏洞,但不幸的是,它太慢了:
SELECT (
MIN( serial_number_basic ) + 1
) as next_available_box
FROM (
SELECT DISTINCT t0.serial_number_basic, t1.serial_number_basic AS number_plus_one
FROM (SELECT * FROM conv WHERE product_id = [productid]) AS t0
LEFT JOIN
(SELECT * FROM conv WHERE product_id = [productid]) AS t1
ON t0.serial_number_basic + 1 = t1.serial_number_basic
) AS sub
WHERE number_plus_one IS NULL;
您可以使用
groupby
删除所有子查询。然后,SELECT子句中的MIN
将仅涵盖每行的一个产品标识:
SELECT
MIN(c.serial_number_basic) + 1 AS next_available_box,
c.product_id
FROM conv c
LEFT JOIN conv AS c1
ON (c1.product_id = c.product_id AND c1.serial_number_basic - 1 = c.serial_number_basic)
WHERE c1.serial_number_basic IS NULL
GROUP BY c.product_id
ORDER BY c.product_id ASC;
返回:
+--------------------+------------+
| next_available_box | product_id |
+--------------------+------------+
| 4 | 1 |
| 2 | 2 |
+--------------------+------------+
从以下数据集:
+---------------------+------------+
| serial_number_basic | product_id |
+---------------------+------------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 5 | 1 |
| 6 | 1 |
| 1 | 2 |
| 3 | 2 |
| 8 | 2 |
| 9 | 2 |
+---------------------+------------+
只有一个警告-您将只得到第一个间隙,如果您的序列号\u basic没有以每种产品的最低可能编号开始,您必须以另一种方式进行验证。表:
create table conv (id int, serial_number_basic int, product_id int);
insert into conv (id, serial_number_basic, product_id) values
(1,1,1),
(2,2,1),
(3,3,1),
(4,5,1),
(5,6,1),
(6,1,2),
(7,3,2),
(8,8,2),
(9,9,2),
(10,2,3)
;
查询:
set @snb := 1;
set @pid := -1;
select sw.product_id, min(sw.serial_number_basic) as lowest_gap
from (
select
case
when @pid != ss.product_id
then @snb := 1
else @snb := @snb + 1
end as serial_number_basic,
@pid := ss.product_id as product_id
from (
select serial_number_basic, product_id
from conv
order by product_id, serial_number_basic
) ss
) sw
left outer join conv
on sw.product_id = conv.product_id
and
sw.serial_number_basic = conv.serial_number_basic
where conv.product_id is null
group by sw.product_id
order by sw.product_id
;
结果:
+------------+------------+
| product_id | lowest_gap |
+------------+------------+
| 1 | 4 |
| 2 | 2 |
| 3 | 1 |
+------------+------------+
3 rows in set (0.00 sec)
编辑:
可能快得多:
set @snb := 1;
set @pid := -1;
select product_id, min(gap) as lowest_gap
from (
select
case
when @pid != product_id
then @snb := 1
else @snb := @snb + 1
end,
case
when @snb != serial_number_basic
then @snb else null
end as gap,
@pid := ss.product_id as product_id
from (
select serial_number_basic, product_id
from conv
order by product_id, serial_number_basic
) ss
) sw
where gap is not null
group by product_id
order by product_id
;
没有任何聚合,没有order by,只是一个简单的外部联接,请尝试:
SELECT MIN(c1.serial_number_basic) + 1
FROM conv c1
LEFT JOIN conv c2
ON c2.serial_number_basic = c1.serial_number_basic+1
AND c2.product_id = c1.product_id
WHERE c1.product_id = 2
AND c2.id IS null
首先,创建一个包含范围
1
到10^5
的所有整数的表:
CREATE TABLE digit
( d INT );
INSERT INTO digit --- without zero
VALUES
(1),(2),(3),(4),(5),
(6),(7),(8),(9) ;
CREATE TABLE number
( n INT PRIMARY KEY );
INSERT INTO number
VALUES
(1) ;
INSERT INTO number --- run this 5 times
SELECT n + (SELECT MAX(n) FROM number)*d
FROM number
CROSS JOIN digit ;
现在,对于某个产品,我们可以运行以下操作:
SELECT
@product_id
, MIN(number.n) AS next_available_serial
FROM
number
LEFT JOIN conv AS c
ON c.serial_number_basic = number.n
AND c.product_id = @product_id
WHERE c.serial_number_basic IS NULL
对于所有产品,这都可以(但是我不知道交叉连接是快还是慢…
):
谢谢,这已经快了两倍!不幸的是,它仍然需要20秒。在包含12个产品的7000个CONV的数据集中。添加了一个可能快得多的版本当间隙填满时会发生什么>请注意,除非您对该值进行局部清理,否则间隙仍有可能重新进入序列;这种类型的问题通常与回滚事务有关,因此这可能不是一个大问题。@例外TV:您有
(产品id,序列号\u basic)
的索引吗?min()不是聚合函数吗?min()不需要排序来找到最小值吗?min可以通过索引查找来得到。很好,很好!我真傻:-(谢谢你!
SELECT
p.product_id
, MIN(number.n) AS next_available_serial
FROM
( SELECT DISTINCT
product_id
FROM conv
) AS p
CROSS JOIN number
LEFT JOIN conv AS c
ON c.serial_number_basic = number.n
AND c.product_id = p.product_id
WHERE c.serial_number_basic IS NULL
GROUP BY p.product_id