2015-08-11 3 views
0

Когда я пытаюсь выполнить запрос под обновлением, он занимает около 40 часов. Поэтому я добавил ограничение по времени (обновить запрос с ограничением по времени). Но все равно требуется почти одинаковое время для завершения. Есть ли способ ускорить это обновление?Как увеличить скорость запроса с помощью команды

EDIT: то, что я действительно хочу сделать, - это получить только журналы между определенными датами и запустить этот запрос обновления для этих записей.

create table user 
(userid varchar(30)); 

create table logs 
(log_time timestamp, 
log_detail varchar(100), 
userid varchar(30)); 

insert into user values('user1'); 
insert into user values('user2'); 
insert into user values('user3'); 
insert into user values(''); 

insert into logs values('no user mentioned','user3'); 
insert into logs values('inserted by user2','user2'); 
insert into logs values('inserted by user3',null); 

Таблица до обновления

log_time |  log_detail | userid | 
..  |-------------------|--------| 
..  | no user mention | user3 | 
..  | inserted by user2 | user2 | 
..  | inserted by user3 | (null) | 

Обновление запроса

update logs join user 
set logs.userid=user.userid 
where logs.log_detail LIKE concat("%",user.userID,"%") and user.userID != ""; 

Обновление запроса с ограничением времени

update logs join user 
set logs.userid = IF (logs.log_time between '2015-08-11 00:39:41' AND '2015-08-01 17:39:44', user.userID, null) 
where logs.log_detail LIKE concat("%",user.userID,"%") and user.userID != ""; 

Таблица после обновления

log_time |  log_detail | userid | 
    .. |-------------------|--------| 
    .. | no user mentione | user3 | 
    .. | inserted by user2 | user2 | 
    ..  | inserted by user3 | user3 | 

EDIT: Оригинальный вопрос Sql update statement with variable.

+0

ли идентификатор пользователя всегда последний элемент в записи журнала? –

+0

@GordonLinoff Да, это всегда последний элемент. – hellzone

+0

добавьте столбец 'ImportantUserID' в таблицу журналов, сохраните там« важный »идентификатор пользователя. Это будет идентификатор, включенный в строку. Добавьте индекс в этот столбец, выполните поиск в этом столбце. Он будет работать намного быстрее, чем 40 часов. Тем не менее, я бы изменил таблицу вокруг, чтобы улучшить ее.Я бы добавил столбец «ChangeType» (I = insert, U = update, D = Delete и т. Д.), Столбец «ChangeUserID» (кто внес изменения) и «AffectedUserID» (кто был изменен) или что-то в этом роде. у вас будет гораздо более высокая скорость, когда вы можете искать индексированные столбцы и не разделять строки с помощью «LIKE». –

ответ

1

таблицы журнала можно легко заполнить с тоннами строк данных каждый месяц и даже лучшее индексирование не поможет, особенно в случае LIKE оператора. Ваш столбец log_detail содержит 100 символов, а ваш поисковый запрос - CONCAT("%",user.userID,"%"). Использование функции в команде SQL может замедлить работу, потому что функция выполняет дополнительные вычисления. И вы пытаетесь найти, если ваш идентификатор пользователя - John, %John%. Таким образом, ваш запрос будет проверять каждую строку в этой таблице, потому что индексы будут бесполезны. Если у вас не было первого %, тогда запрос мог бы эффективно использовать свои индексы. Фактически ваш запрос сделает , а не INDEX SEEK.

Для получения дополнительной информации об этих понятиях см:

Index Seek VS Index Scan

Query tuning a LIKE operator

Хорошо, что вы можете сделать по этому поводу? Две стратегии.

  • Вариант 1, чтобы ограничить количество строк, которые вы ищете через. У вас была правильная идея с использованием ограничений по времени, чтобы уменьшить количество строк, которые нужно выполнить для поиска: . То, что я хотел бы предложить, заключается в том, чтобы установить ограничения времени в качестве первого выражения в вашем предложении WHERE. Большинство баз данных сначала выполняют первое выражение. Поэтому, когда выдает второе выражение, оно будет проверять только строки, возвращаемые первым выражением.

    update logs join user 
    set logs.userid=user.userid 
    where logs.log_time between '2015-08-01' and '2015-08-11' 
    and logs.log_detail LIKE concat('%',user.userID,'%') 
    
  • Вариант 2 зависит от вашего контроля над базой данных. Если у вас есть общее управление (у вас есть время и деньги, у MySQL есть функция, называемая Auto-Sharding. Это доступно в MySQL Cluster и MySQL Fabric. Я не буду переходить через эти продукты в деталях, поскольку ссылки предоставлены ниже можно объяснить себя намного лучше, чем я мог бы подвести итог, но идея, стоящая за Sharding, состоит в том, чтобы разбить строки на горизонтальные таблицы, так сказать. Идея заключается в том, что вы - , не просматривая длинную таблицу базы данных, но вместо этого через несколько столов-сестер одновременно. Поиск по 10 столам из 10 миллионов строк быстрее, чем поиск по 1 таблице из 100 миллионов строк.

Database Sharding - Wikipedia

MySQL Cluster

MySQL Fabric

1

Во-первых, правильное место, чтобы поставить ограничение по времени находится в пункте where, не if:

update logs l left join 
     user u 
     on l.log_detail LIKE concat("%", u.userID) 
    set l.userid = u.userID 
where l.log_time between '2015-08-11 00:39:41' AND '2015-08-01 17:39:44'; 

Если вы хотите установить другие NULL сделать это раньше:

update logs l 
    set l.userid = NULL 
    where l.log_time not between '2015-08-11 00:39:41' AND '2015-08-01 17:39:44'; 

Но если вы действительно хотите, чтобы это было быстро, вам нужно использовать индекс для соединения. Вполне возможно, что это будет использовать индекс users(userid):

update logs l left join 
     user u 
     on cast(substring_index(l.log_detail, ' ', -1) as signed) = u.userID 
    set l.userid = u.userID 
where l.log_time between '2015-08-11 00:39:41' AND '2015-08-01 17:39:44'; 

Посмотрите на explain на эквивалентном select. Очень важно, чтобы cast() был того же типа, что и UserId.

+0

Есть несколько записей с значением «" (не нуль, но пробел) userId в таблице пользователя. Когда я запускаю ваш запрос, он обновляет столбец logs.userID со значением «». – hellzone

0

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

update logs 
set userid = substring_index(log_detail, ' ', -1) 
where log_time between '2015-08-11 00:39:41' AND '2015-08-01 17:39:44' 
and not userid <=> substring_index(log_detail, ' ', -1); 
+0

MySQL фактически не обновит запись, которая не нуждается в обновлении. Ваше решение сделает его медленнее. Из руководства MySQL: 'Если вы установите столбец для значения, которое оно имеет в настоящее время, MySQL замечает это и не обновляет его'. –

1

Вы можете добавить новый столбец с именем log_detail_reverse где можно установить триггер так, что при вставке новой строки, вы также вставить log_detail столбец в обратном порядке символов, используя функцию MySQL reverse. Когда вы выполняете свой запрос на обновление, вы также изменяете поиск по идентификатору пользователя. Чистым эффектом является то, что вы затем преобразуете в INDEX SEEK, что будет намного быстрее.

update logs join user 
set logs.userid=user.userid 
where logs.log_time between '2015-08-01' and '2015-08-11' 
and logs.log_detail_reverse LIKE concat(reverse(user.userID), '%') 

MySQL Trigger

Trigger может быть что-то вроде:

DELIMITER // 

CREATE TRIGGER log_details_in_reverse 
AFTER INSERT 
ON logs FOR EACH ROW 

BEGIN 

DECLARE reversedLogDetail varchar(100); 
DECLARE rowId int; <-- you don't have a primary key in your example, but I'm assuming you do have one. If not, you should look into adding it. 

-- Reverse the column log_detail and assign it to the declared variable 
SELECT reverse(log_detail) INTO reversedLogDetail; 
SELECT mysql_insert_id() INTO rowId; 

-- Update record into logs table 
UPDATE logs 
SET log_detail_reverse = reversedLogDetail 
WHERE log_id = rowId; 

END; // 

DELIMITER ; 
Смежные вопросы