2011-07-15 68 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