#mysql #sql #запрос
Есть таблица sample. Важный уникальный индекс UNIQUE( laboratory_prefix_id, sample_number ). Последний задаёт группу, в пределах которой не может быть повторяющихся sample_number. Вопрос следующий - как быстро получить список пропущенных sample_number в пределах запрошенной группы laboratory_prefix_id? И если не список, то хотя бы 1 наименьшее значение. Например, если искать в группе laboratory_prefix_id=5, то максимальный sample_number=15, но перед ним пропущены значения 11, 12, 13, 14 - их (или хотя бы значение 11) хотелось бы как-то получить (т.е. получить просто sample number пропущенных значений). Как это быстро сделать? Дамп таблицы для примера прикладываю CREATE TABLE `sample` ( `id` Int( 10 ) UNSIGNED AUTO_INCREMENT NOT NULL, `person_id` Int( 10 ) UNSIGNED NOT NULL, `laboratory_prefix_id` TinyInt( 3 ) UNSIGNED NOT NULL DEFAULT '1', `sample_number` Smallint( 5 ) UNSIGNED NOT NULL, `total_cost` Decimal( 6, 2 ) NOT NULL DEFAULT '0.00', `completion_date` Date NULL, `barcode` VarChar( 15 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, `country_id` TinyInt( 3 ) UNSIGNED NOT NULL DEFAULT '20', `sample_priority` TinyInt( 1 ) UNSIGNED NOT NULL DEFAULT '1', `sample_note` VarChar( 255 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, `registration_date` Date NOT NULL, PRIMARY KEY ( `id` ), CONSTRAINT `UK_sample` UNIQUE( `laboratory_prefix_id`, `sample_number` ) ) CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT 'Образцы' ENGINE = InnoDB AUTO_INCREMENT = 57; INSERT INTO `sample`(`id`,`person_id`,`laboratory_prefix_id`,`sample_number`,`total_cost`,`registration_date`,`completion_date`,`barcode`,`country_id`,`sample_priority`,`sample_note`) VALUES ( '15', '120', '8', '155', '0.00', '2017-08-18', NULL, '12-55fff', '20', '3', 'sdfsdfsdf' ), ( '22', '120', '7', '1', '0.00', '2017-08-11', NULL, NULL, '20', '1', NULL ), ( '32', '120', '7', '10', '0.00', '2017-08-19', NULL, NULL, '20', '1', NULL ), ( '33', '165', '5', '1', '0.00', '2017-08-19', NULL, NULL, '20', '2', NULL ), ( '34', '165', '5', '2', '0.00', '2017-08-19', NULL, NULL, '20', '2', NULL ), ( '35', '166', '5', '3', '0.00', '2017-08-19', NULL, NULL, '20', '3', NULL ), ( '36', '166', '5', '4', '0.00', '2017-08-19', NULL, NULL, '20', '3', NULL ), ( '37', '167', '5', '5', '0.00', '2017-08-19', NULL, NULL, '20', '2', '6лоло' ), ( '38', '168', '5', '6', '0.00', '2017-08-19', NULL, NULL, '20', '1', NULL ), ( '39', '168', '5', '7', '0.00', '2017-08-19', NULL, NULL, '20', '1', NULL ), ( '40', '168', '5', '8', '0.00', '2017-08-19', NULL, NULL, '20', '1', NULL ), ( '41', '168', '5', '9', '0.00', '2017-08-19', NULL, NULL, '20', '1', NULL ), ( '42', '168', '5', '10', '0.00', '2017-08-19', NULL, NULL, '20', '1', NULL ), ( '43', '173', '7', '2', '0.00', '2017-08-20', NULL, 'к', '17', '2', 'ккк' ), ( '44', '173', '5', '15', '0.00', '2017-08-18', NULL, 'ne', '20', '2', '=' ), ( '47', '180', '8', '5', '0.00', '2017-08-20', NULL, NULL, '20', '2', NULL ), ( '49', '120', '8', '161', '0.00', '2017-08-18', NULL, '12-55fff', '20', '2', 'sdfsdfsdf' ), ( '54', '120', '6', '212', '0.00', '2017-09-01', NULL, NULL, '20', '2', NULL ), ( '55', '120', '6', '213', '0.00', '2017-09-01', NULL, NULL, '20', '3', NULL ), ( '56', '120', '6', '214', '0.00', '2017-09-01', NULL, NULL, '20', '2', NULL );
Ответы
Ответ 1
SELECT @count FROM sample, (select @count := 0) dummy WHERE sample.laboratory_prefix_id = 5 AND (@count := @count+1) < sample.sample_number ORDER BY sample.sample_number ASC LIMIT 1; Не работает в случае, если для заданного sample.laboratory_prefix_id нет ни одной записи. Если начальное значение не равно 1, следует откорректировать как псевдотаблицу dummy, так и условие во WHERE. Вот более универсальный запрос (соответствует условию типа "найти первый свободный в группе laboratory_prefix_id = 5, но не менее sample.sample_number = 4"): SET @laboratory_prefix_id = 5; SET @min_sample_number = 4; SELECT @count FROM sample, (select @count := @min_sample_number-1) dummy WHERE sample.laboratory_prefix_id = @laboratory_prefix_id AND sample.sample_number > @min_sample_number-1 AND (@count := @count+1) < sample.sample_number ORDER BY sample.sample_number ASC LIMIT 1; Недостаток - тот же. Ну и запрос, избавленный от этого недостатка: SET @laboratory_prefix_id = 5; SET @min_sample_number = 4; ( SELECT @count FROM sample, (select @count := @min_sample_number-1) dummy WHERE sample.laboratory_prefix_id = @laboratory_prefix_id AND sample.sample_number > @min_sample_number-1 AND (@count := @count+1) < sample.sample_number ORDER BY sample.sample_number ASC LIMIT 1 ) UNION ALL ( SELECT @min_sample_number ) ORDER BY 1 DESC LIMIT 1;Ответ 2
select s1.sample_number+1 from sample s1 left join sample s2 on s2.sample_number = s1.sample_number+1 AND s2.laboratory_prefix_id=5 where s1.laboratory_prefix_id=5 and s2.id is null order by s1.sample_number limit 1;
Комментариев нет:
Отправить комментарий