Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/80.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mysql 优化慢速查询以查找序列中的漏洞_Mysql_Sql - Fatal编程技术网

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