如何改进存储过程查询

时间:2011-01-02 08:02:09

标签: mysql sql stored-procedures

我有存储过程,如果可以插入或更新记录,则检查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 :(得分:0)

至少你可以使用UPSERT而不是检查行的存在然后插入它。

答案 1 :(得分:0)

我通常使用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;  

还有一些其他事情可以做,以便进一步加快查询速度。 like子句比equals慢得多。如果可能,将 CitySynonyms更改为 CitySynonyms = SP_CitySynonyms ,将CONCAT('%,',SP_CitySynonyms,',%')等CitySynonyms更改为。 假设您提供以“D”开头的同义词,数据库可以索引直接搜索到D,甚至不会查看其他记录。 当然,要使用它,您需要在表格上包含CitySynonyms列的索引。如果使用Like子句,数据库将运行instring()检查 针对表中每一行的列,这是处理相当多行时的主要性能。

从电话目录的角度来看,如果我想要一个名字叫史密斯的人,我会在书的后面查找索引,并发现S在第600页开始(称为搜索)。我不关心前599页,甚至不看任何名字。如果没有索引,我将不得不遍历每一页,直到我从S开始,然后是SM,等等(称为扫描)。但LIKE子句就像搜索包含SMITH的姓氏一样,该姓氏可能是以下任何一个(来自Wiki的名字)BlackSmith Coppersmith GoldSmith HammerSmith Smither Smithers等。正如您所看到的那样,名字遍布各处。需要检查每个名称以查看它是否包含SMITH。它是你可以要求别人做的最不酷的事情之一,但我们要求数据库一直这样,然后问为什么它很慢。 :)

另一件事是(UpdatedDate&lt; SP_CityUpdatedDate或UpdatedDate = SP_CityUpdatedDate),我已将其更改为 UpdatedDate&lt; = SP_CityUpdatedDate 。 真的需要检查吗?因为如果你不这样做,你可以删除整个if exists部分因为你知道它存在,如果执行到达那个点,那么因为记录存在而返回false的第一个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;

编辑 - 在回答以下评论中的问题时添加。

在我继续之前,应该

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

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

答案 2 :(得分:0)

我想指出的是,只要在字段中有逗号分隔列表,就需要重构数据库以改为使用相关表。您永远无法在分隔字段上搜索或尝试更新字段。数据库设计的最初规则之一是永远不会在一个字段中保存多条信息。对于包含子表的所有内容,您将获得更快的性能来保存信息。