оптимизация медленного запроса, чтобы найти дыру в последовательности - PullRequest
3 голосов
/ 15 июля 2011

У меня есть таблица, которая выглядит так:

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

serial_number_basic - это порядковый номер, который подсчитывает каждый раз, когда требуется новый номер. В прошлом можно было оставить пустыми целые диапазоны этого числа, а следующее число было MAX() + 1.

В связи с изменениями требований, пустые отверстия должны быть заполнены. Серийный номер_основы зависит, конечно, от идентификатора продукта. Каждый продукт имеет свою последовательность serial_number_basics. Проблема в том, чтобы найти дыры.

Этот запрос находит дыры в зависимости от каждого [продукта], но, к сожалению, он слишком медленный:

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 ]

2 голосов
/ 15 июля 2011

Без агрегатов, без упорядочивания, просто простое внешнее объединение, попробуйте:

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 голос
/ 15 июля 2011

Вы можете вырезать все подзапросы, используя GROUP BY.Тогда 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;

Возвращает:

+--------------------+------------+
| 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 не начинается с минимально возможного числа для каждого продукта.

0 голосов
/ 16 июля 2011

Сначала создайте таблицу со всеми целыми числами в диапазоне от 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
0 голосов
/ 15 июля 2011

Таблица:

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)

EDIT:

Вероятно, намного быстрее:

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
;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...