@hjk

Как ускорить запрос?

Добрый день.

Есть таблица следующего формата
CREATE TABLE `linestatuslog` (
	`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
	`line` CHAR(4) NULL DEFAULT NULL,
	`status` CHAR(4) NULL DEFAULT NULL,
	`user` VARCHAR(45) NULL DEFAULT NULL,
	`startDate` DATETIME NULL DEFAULT NULL,
	PRIMARY KEY (`id`),
	INDEX `line_idx` (`line`),
	INDEX `status_idx` (`status`),
	INDEX `user_idx` (`user`),
	INDEX `startDate_idx` (`startDate`)
)


В таблице ведётся простой лог, добавляются данные по такому принципу: какой status был установлен для line и startDate когда. Стоит отметить, что количество line и status ограничено — около 10-15 разных наименований. Всего записей в таблице около ста тысяч.

Появилась потребность добавить поле endDate, в котором хотелось бы получить дату окончания конкретного статуса для конкретной линии. Датой окончания положено считать наличие следующей записи для этой же линии, но уже с другим статусом.

В общем, алгоритмически это несложная задача и она, в принципе, решается хранимой процедурой:
BEGIN
	DECLARE rowCount INT;
	DECLARE curRow INT;
	DECLARE logId INT;
	DECLARE lineName VARCHAR(4);
	DECLARE _startDate DATETIME;
	DECLARE _endDate DATETIME;
	
	SET curRow=1;
	
	myLoop: WHILE (curRow<1001) DO
		SET curRow=curRow+1;
		SET logId=(select id from linestatuslog where endDate is null order by startDate asc limit 1);
		set lineName=(select line from linestatuslog where id=logId);
		set _startDate=(select startDate from linestatuslog where id=logId);
		set _endDate=(select startDate from linestatuslog where line=lineName and startDate>_startDate order by startDate asc limit 1);
		IF (_endDate is null) THEN
			set _endDate='2016-01-01 00:00:00';
		END IF;
		UPDATE linestatuslog set endDate=_endDate where id=logId;
	END WHILE myLoop;
END


Как видно из кода выше, цикл ограничен только 1000 проходами, потому что даже эта 1000 работает достаточно долго (около 4х минут).

Вопрос чисто академический — как это ускорить? И что можно почитать для понимания таких вещей? Задача вроде как была решена, но долго и неэффективно, а поскольку в SQL я не силён, возник такой вопрос.
  • Вопрос задан
  • 187 просмотров
Решения вопроса 1
можно так:
UPDATE linestatuslog s, (SELECT * FROM linestatuslog ORDER BY startDate) n
SET s.endDate = n.startDate
WHERE n.line = s.line AND n.startDate > s.startDate
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
mgyk
@mgyk
Для вот такой таблицы
+----+------+--------+------+---------------------+
| id | line | status | user | startDate           |
+----+------+--------+------+---------------------+
|  1 | TEST | star   | NULL | 2015-04-29 12:54:30 |
|  2 | TEST | stop   | NULL | 2015-04-29 12:54:59 |
|  3 | FOO  | stop   | NULL | 2015-04-29 13:07:29 |
|  4 | FOO  | star   | NULL | 2015-04-29 12:55:21 |
+----+------+--------+------+---------------------+


select id, if(@line=line AND @status!=status,@startDate, NULL) as endDate, 
@line:=line as line, @status:=status as status, @startDate:=startDate as startDate
FROM (select id, line,status, startDate from linestatuslog order by line, status, startDate) as c;


+----+---------------------+------+--------+---------------------+
| id | endDate             | line | status | startDate           |
+----+---------------------+------+--------+---------------------+
|  4 | NULL                | FOO  | star   | 2015-04-29 12:55:21 |
|  3 | 2015-04-29 12:55:21 | FOO  | stop   | 2015-04-29 13:07:29 |
|  1 | NULL                | TEST | star   | 2015-04-29 12:54:30 |
|  2 | 2015-04-29 12:54:30 | TEST | stop   | 2015-04-29 12:54:59 |
+----+---------------------+------+--------+---------------------+


Тут стоит иметь ввиду что 1) данные должны быть отсортированы сначала 2) значение переменых устанавливаются по порядку, по-этому endDate генерируется первой, так как тут мы можем обратиться за предыдущими значениями.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы