как улучшить запрос хранимой процедуры - PullRequest
1 голос
/ 02 января 2011

Я сохранил процедуру, которая проверяет на сервере MySql, можно ли вставлять или обновлять записи.для времени запуска это так быстро, через 5-10 минут это так медленно .. я бегу за 3000 записей и выполнение SP так плохо, через 1-1,5 часа это закончено ... мой вопрос, как я могу улучшитьчто ??

спасибо.

хранимая процедура:

DELIMITER $$ 
DROP PROCEDURE IF EXISTS test.SPInsertUpdateCity$$ 
CREATE DEFINER=root@localhost PROCEDURE SPInsertUpdateCity( 
in SP_CityName VARCHAR(100) charset utf8, 
in SP_CitySynonyms mediumtext charset utf8, 
in SP_CityNumberPostOffice varchar(100), 
in SP_CityUpdatedDate date) 
BEGIN 
    if(not exists(select CityID from city where CityName = SP_CityName)) then 
        insert into city(CityName, CitySynonyms, CityNumberPostOffice ,UpdatedDate) 
        values(SP_CityName, CONCAT(',',SP_CitySynonyms, ','),SP_CityNumberPostOffice,SP_CityUpdatedDate);
    else if((exists(select cityId from city 
    where 
        CityName = SP_CityName and 
        (UpdatedDate < SP_CityUpdatedDate or UpdatedDate = SP_CityUpdatedDate))) and 
        not exists(SELECT CitySynonyms FROM city 
        WHERE 
            CitySynonyms in(select CitySynonyms from city where CitySynonyms like CONCAT('%,',SP_CitySynonyms,',%')))) 
            then 
            update city set 
                CitySynonyms = CONCAT(CitySynonyms,SP_CitySynonyms,','),
                UpdatedDate = SP_CityUpdatedDate; 
    end if; 
    end if; 
END$$ 
DELIMITER ; 

Ответы [ 3 ]

0 голосов
/ 02 января 2011

Я обычно работаю с MSSQL, но из того, что я могу сказать, в вашем другом разделе, который дает вам хит.Вы нажимаете на таблицу 3 раза при каждом ее запуске, когда она может быть вдвое больше.

Это ваш исходный запрос, переформатированный для простого сравнения.

    if(not exists(  select  CityID 
                    from    city 
                    where   CityName = SP_CityName)) then
        insert into city(   CityName, CitySynonyms, CityNumberPostOffice ,UpdatedDate) 
                values  (   SP_CityName, CONCAT(',',SP_CitySynonyms, ','), SP_CityNumberPostOffice, SP_CityUpdatedDate);
    else if((exists(    select  cityId 
                        from    city      
                        where   CityName = SP_CityName and 
                                (UpdatedDate < SP_CityUpdatedDate or UpdatedDate = SP_CityUpdatedDate))) and          
            not exists( SELECT CitySynonyms 
                        FROM city          
                        WHERE CitySynonyms in ( select CitySynonyms 
                                                from city 
                                                where CitySynonyms like CONCAT('%,',SP_CitySynonyms,',%')))) then
        update city set CitySynonyms = CONCAT(CitySynonyms,SP_CitySynonyms,','),                 
                        UpdatedDate = SP_CityUpdatedDate;      
    end if;      
    end if;  

Это новый

if(not exists(  select  CityID 
                from    city 
                where   CityName = SP_CityName)) then
    insert into city(   CityName, CitySynonyms, CityNumberPostOffice ,UpdatedDate) 
            values  (   SP_CityName, CONCAT(',',SP_CitySynonyms, ','), SP_CityNumberPostOffice, SP_CityUpdatedDate);
else if((exists(    select  cityId 
                    from    city      
                    where   CityName = SP_CityName and 
                            UpdatedDate <= SP_CityUpdatedDate)) and          
        not exists( select  CitySynonyms 
                    from    city 
                    where   CitySynonyms like CONCAT('%,',SP_CitySynonyms,',%'))) then
    update city set CitySynonyms = CONCAT(CitySynonyms,SP_CitySynonyms,','),                 
                    UpdatedDate = SP_CityUpdatedDate;      
end if;      
end if;  

Есть еще несколько вещей, которые можно сделать, чтобы ускорить запрос.Подобное предложение намного медленнее, чем равные.Если возможно, измените CitySynonyms, например, CONCAT ('%,', SP_CitySynonyms, ',%') на CitySynonyms = SP_CitySynonyms .Скажем, вы предоставляете синоним, начинающийся с «D», база данных может индексировать поиск прямо на D и даже не будет смотреть на другие записи.Конечно, чтобы это работало, вам понадобится индекс для таблицы, который включает столбец CitySynonyms.Если вы используете предложение Like, база данных будет запускать проверку instring () для столбца для каждой строки в таблице, что является основным ударом по производительности при работе с несколькими строками.

Думайте об этом с точки зрения телефонного справочника. Если я хочу человека с фамилией Смит, я бы посмотрел указатель в конце книги и обнаружил, что буква S начинается на странице 600 (называется поиском).Я не забочусь о первых 599 страницах и даже не смотрю на них.Без индекса мне пришлось бы просматривать каждую страницу, пока не добрались до имен, начинающихся с S, затем SM и т. Д. (Называемых сканированием).Но предложение LIKE похоже на поиск фамилии, содержащей SMITH, которая может быть любой из следующих (имя из Wiki) BlackSmith Coppersmith GoldSmith HammerSmith Smither Smithers и т. Д.Как видите, имена повсюду.Каждое имя нужно будет проверить, чтобы увидеть, содержит ли оно SMITH.Это одна из самых нехороших вещей, которую вы можете попросить кого-то сделать, но мы просим базу данных постоянно делать подобные вещи, а затем спрашиваем, почему это медленно.:)

Другая вещь - это (updatedDate , которую я изменил на updatedDate <= SP_CityUpdatedDate </strong>.Эта проверка действительно нужна?Потому что, если вы этого не сделаете, вы можете удалить весь этот раздел, если существует, потому что вы знаете, что он существует, если выполнение достигает этой точки в качестве самой первой проверки if, которая вернула false, поскольку существует запись.Так что теперь else if - это одно попадание в таблицу вместо трех.

if(not exists(  Select  CityID 
                from    city 
                where   CityName = SP_CityName)) then          

    insert into city(   CityName, CitySynonyms, CityNumberPostOffice ,UpdatedDate) 
            values  (   SP_CityName, CONCAT(',',SP_CitySynonyms, ','), SP_CityNumberPostOffice, SP_CityUpdatedDate);     

else if(not exists( select  CitySynonyms 
                    from    city 
                    where   CitySynonyms like CONCAT('%,',SP_CitySynonyms,',%'))) then              

    update city set CitySynonyms = CONCAT(CitySynonyms,SP_CitySynonyms,','),                 
                    UpdatedDate = SP_CityUpdatedDate;      
end if;      
end if;

EDIT - добавлено в ответ на вопросы в комментариях ниже.

Прежде чем идти дальше, следует

update city set CitySynonyms = CONCAT(CitySynonyms,SP_CitySynonyms,','), 
                UpdatedDate = SP_CityUpdatedDate;

be

update city set CitySynonyms = CONCAT(CitySynonyms,SP_CitySynonyms,','), 
                UpdatedDate = SP_CityUpdatedDate
where CityName = SP_CityName; 

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

Что касается аналогичного утверждения.Я думаю, что только что понял, что происходит.Поправьте меня если я ошибаюсь.Столбец CitySynonyms - это большое текстовое поле в вашей таблице, в котором несколько значений разделены запятыми, и вы пытаетесь найти совпадение в этой строке с помощью оператора like.

Если это так, можно линемного изменить структуру таблицы?Это означает перемещение столбца CitySynonyms во вторую таблицу.

CREATE TABLE city ( CityID int(20) NOT NULL AUTO_INCREMENT, 
                    CityName varchar(100) NOT NULL, 
                    CityNumberPostOffice varchar(100) DEFAULT NULL, 
                    UpdatedDate date DEFAULT NULL, 
PRIMARY KEY (CityID) ) ENGINE=InnoDB DEFAULT CHARSET=utf8

CREATE TABLE CitySynonym ( CitySynonymID int(20) NOT NULL AUTO_INCREMENT,
                           CityID int(20) NOT NULL ,                    
                           CitySynonym varchar(100) NOT NULL,
PRIMARY KEY (CitySynonymID) ) ENGINE=InnoDB DEFAULT CHARSET=utf8

Для каждого города можно использовать несколько синонимов.Его становится намного проще в использовании и обслуживании, не говоря уже о том, что предложение LIKE больше не потребуется.

Example:
City Table
CityID    CityName
1         Brisbane
2         Syndney

City Synonym Table
CitySynoymnID    CityID  Synonym
1                1       BNE
2                1       BRISSY
3                2       SYD
0 голосов
/ 06 мая 2011

Хочу отметить, что каждый раз, когда у вас есть список с разделителями-запятыми в поле, вам нужно реорганизовать базу данных, чтобы иметь связанную таблицу. Вы никогда не сможете получить хорошую производительность при поиске в поле с разделителями или при попытке обновить его. Одно из самых первых правил проектирования баз данных - НИКОГДА не хранить больше одной информации в поле. Вы будете иметь намного более высокую производительность на всем, с дочерней таблицей для хранения информации.

0 голосов
/ 02 января 2011

По крайней мере, вы можете использовать UPSERT вместо проверки наличия строки и затем вставить ее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...