2011-07-15 67 views
3

洞我有一个表,看起来像这样:优化慢速查询找到序列

id | serial_number_basic | product_id 
------------------------------------- 

的serial_number_basic是一个流水号,其每次需要一个新的数字一次计数。过去有可能将此号码的整个范围留空,并且下一个号码是MAX() + 1

由于要求的变化,现在应该填补空洞。 serial_number_basic当然取决​​于product_id。每个产品都有自己的serial_number_basics序列。问题是要找到漏洞。

此查询确实发现根据每个的[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; 
+0

当间隙填满了,会发生什么> – dkretz

+0

请注意,除非你是ocasionally消毒这个值,它可能仍然是可能的缝隙悄悄放回序列;这类问题通常与回滚事务有关,所以这可能不是一个大问题。 –

+1

@Exceptiondev:你有'(product_id,serial_number_basic)'上的索引吗? –

回答

2

没有任何聚集,没有秩序,通过,只是一个简单的外连接,尝试:

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 
+0

是不是min()一个聚合函数?而不要min()需要排序才能找到最小值? –

+0

只需索引查找即可派生MIN。 – dkretz

+0

作品,太棒了!我很愚蠢:-(谢谢你! – Exceptiondev

1

您可以通过使用GROUP BY切出所有的子查询。从数据集

+--------------------+------------+ 
| next_available_box | product_id | 
+--------------------+------------+ 
|     4 |   1 | 
|     2 |   2 | 
+--------------------+------------+ 

:那么MIN SELECT子句将只覆盖每一行的一个PRODUCT_ID:

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; 

返回

+---------------------+------------+ 
| serial_number_basic | product_id | 
+---------------------+------------+ 
|     1 |   1 | 
|     2 |   1 | 
|     3 |   1 | 
|     5 |   1 | 
|     6 |   1 | 
|     1 |   2 | 
|     3 |   2 | 
|     8 |   2 | 
|     9 |   2 | 
+---------------------+------------+ 

只是一个警告 - 你只会得到第一个差距,如果你的serial_number_basic没有以每个产品的最小可能数量开始,你将不得不另外验证。

+0

谢谢,这已经快了2倍!不幸的是它仍然需要20秒。在一个包含12个产品的7000次转换的数据集中。 – Exceptiondev

0

表:

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 
; 
+0

增加了一个可能更快的版本 –

0

首先,创建与范围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 

对于这样做的所有产品(但我不知道如果CROSS JOIN将快或慢地狱......):

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