优化慢查询以按顺序查找孔

时间:2011-07-15 14:09:02

标签: mysql sql

我有一张看起来像这样的表:

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;

4 个答案:

答案 0 :(得分:2)

没有任何聚合,没有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 :(得分:1)

您可以使用GROUP BY删除所有子查询。然后SELECT子句中的MIN将仅涵盖每行的一个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;

返回:

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

只需注意一点 - 您只能获得第一个缺口,如果您的serial_number_basic没有以每个产品的最低编号开始,则必须验证另一种方式。

答案 2 :(得分: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
;

答案 3 :(得分:0)

首先,创建一个包含110^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