powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / MySQL [игнор отключен] [закрыт для гостей] / Оптимизация запроса
5 сообщений из 5, страница 1 из 1
Оптимизация запроса
    #39159578
Pim.
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Всем доброго времени суток!

Помогите пожалуйста оптимизировать запрос на INSERT.

Есть таблица locations в которой поле location представляет собой полный адрес типа "Республика Бурятия Район Тункинский Село Кырен Улица Ленина Дом 156.". Из вне в формате csv приходят адреса разных подключений в таком формате. Так как регион и город в отдельное поле там вынести не могут, пришлось самому этим заниматься.
В отдельных таблицах у меня есть список всех регионов и большинства городов.
Написал две функции, которые для входной строки с полным адресом выводит один из имеющихся с БД городов либо регионов.

Проблема в том, что эти функции я повесил на триггер BEFORE INSERT и когда я делаю INSERT IGNORE в таблицу locations (чтобы вставить только те адреса, которых еще нет в базе и для них вычислить город и регион), триггер вызывается для всех 3500 строк, из-за чего запрос длится около 50 секунд, хотя по факту ни одна новая строка не вставляется.

Generated column использовать не могу, т.к. версия MySQL 5.6

Код: sql
1.
INSERT IGNORE INTO locations (location) SELECT location FROM atms_list;



Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
CREATE TABLE `locations` (
  `location_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `location` varchar(255) NOT NULL,
  `region_id` tinyint(3) unsigned NOT NULL DEFAULT '0',
  `city_id` smallint(5) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`location_id`),
  UNIQUE KEY `location_UNIQUE` (`location`),
  KEY `fk_atms_cities_idx` (`city_id`),
  KEY `fk_atms_regions_idx` (`region_id`),
  CONSTRAINT `fk_atms_cities` FOREIGN KEY (`city_id`) REFERENCES `cities` (`city_id`),
  CONSTRAINT `fk_atms_regions` FOREIGN KEY (`region_id`) REFERENCES `regions` (`region_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4102 DEFAULT CHARSET=utf8;

CREATE TRIGGER `locations_insert` BEFORE INSERT ON `locations` FOR EACH ROW BEGIN
SET NEW.city_id=get_city(NEW.location);
SET NEW.region_id=get_region(NEW.location);
END;



Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
CREATE FUNCTION `get_city`(in_location VARCHAR(255)) RETURNS smallint(5) unsigned
    READS SQL DATA
BEGIN
	DECLARE out_city SMALLINT(5) UNSIGNED DEFAULT 0;
	DECLARE done INT DEFAULT FALSE;
	DECLARE city VARCHAR(255);
	DECLARE id SMALLINT(5);
	DECLARE cur CURSOR FOR SELECT city_id, city_name FROM cities;
	DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
	SET @a = 0;

	OPEN cur;
	
	read_loop: LOOP
		FETCH cur INTO id, city;
		IF done THEN
      LEAVE read_loop;
    END IF;
		IF (SELECT in_location REGEXP CONCAT('[[:<:]]',city,'[[:>:]]')) = 1 THEN
			SELECT id INTO out_city;
			LEAVE read_loop;
    END IF;
	END LOOP;

	CLOSE cur;
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39159658
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Pim.Проблема в том, что эти функции я повесил на триггер BEFORE INSERT и когда я делаю INSERT IGNORE в таблицу locations (чтобы вставить только те адреса, которых еще нет в базе и для них вычислить город и регион), триггер вызывается для всех 3500 строк, из-за чего запрос длится около 50 секунд, хотя по факту ни одна новая строка не вставляется.


Это не страшно и вполне допустимо.

[8]> (coerce (/ (/ 3500 50)) 'float)
0.014285714

14 милисекунд на запись.

Нормально.
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39159659
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Pim.,

Хотя вот эту

CREATE FUNCTION `get_city`(in_location VARCHAR(255)) RETURNS smallint(5) unsigned

функцию можно и получше сделать, нужно уйти от перебора в цикле по курсору всех записей из cities
и выборке нужной записи из cities одним запросом с использованием индекса.
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39159665
miksoft
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Pim.,

А зачем делать курсором и процедурой то, что можно сделать одним запросом?

Что-то типа такого:
Код: sql
1.
2.
3.
4.
CREATE TRIGGER `locations_insert` BEFORE INSERT ON `locations` FOR EACH ROW BEGIN
SELECT city_id FROM cities WHERE NEW.location REGEXP CONCAT('[[:<:]]',city_name,'[[:>:]]') LIMIT 1 INTO NEW.city_id;
...
END;

Это, правда, не избавит от фулскана таблицы cities, но хоть накладных расходов поменьше будет.
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39159709
Pim.
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MasterZiv, miksoft,

Спасибо большое!

Я что-то с дуру решил мыслить процедурным языком))

Переписал функции, но в триггере прописал селекты, ибо вызов функции накладывает лишнюю секунду. Хоть это и не принципиально, но глазу приятно. В итоге с 50 секунд, инсерт сократился до 2,6 секунд.

Хоть это уже для меня и готовое решение проблемы, все же, нет ли у кого-нибудь идей, как избежать фулскана таблицы при инсерте?

Вот, что получилось:
Код: sql
1.
2.
3.
4.
5.
6.
CREATE TRIGGER locations_insert BEFORE INSERT ON locations
FOR EACH ROW 
BEGIN
SET NEW.city_id = IFNULL((SELECT city_id FROM cities WHERE NEW.location REGEXP CONCAT('[[:<:]]',city_name,'[[:>:]]') LIMIT 1),0);
SET NEW.region_id = IFNULL((SELECT region_id FROM regions WHERE LOCATE(region_name,NEW.location) <> 0 LIMIT 1),0);
END;
...
Рейтинг: 0 / 0
5 сообщений из 5, страница 1 из 1
Форумы / MySQL [игнор отключен] [закрыт для гостей] / Оптимизация запроса
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]