zoukankan      html  css  js  c++  java
  • 数据库性能优化地址搜索

    问题:

    有一张表TBAddress:

    字段名

    数据类型

    说明

    ID

    int

    主键

    Parent

    int

    父ID

    LevelNo

    smallint

    地址所在的层(范围:0 至 5)

    Name

    nvarchar(50)

    地址名称

    表数据如下:

    问题:给出一个字符串如“广 大”,找出地址全路径中包含有“广” 和“大”的所有数据,結果如:

    ID

    AddressPath

    26996

    山西省/大同市/广灵县/梁庄乡

    26998

    山西省/大同市/广灵县/蕉山乡

    44033

    广东省/广州市/黄埔区/大沙街道

    13623

    广东省/广州市/番禺区/大石街道

    13625

    广东省/广州市/番禺区/大龙街道

    … …

    … …

    要求查询耗时控制在秒级范围。

    下面提供测试用的表和數據,创建表和插入数据脚本:

    View Code

     Insert Data

    附:【地址全路径】:

    “新疆维吾尔自治区巴音郭楞蒙古自治州若羌县依吞布拉克镇”

    “江西省吉安市吉水县阜田镇”

    方法1


    1. 先搜索出包字段Name中含有“广”、“大”的所有地址记录存入临时表#tmp。
    2. 再找出#tmp中各个地址到Level 1的全路径。
    3. 根据步骤2所得的结果,筛选出包含有“广”和“大”的地址路径。
    4. .根据步骤3筛选的结果,查询所有到Level n(n为没有子地址的层编号)的地址全路径。

    方法1脚本(up_SearchAddressByNameV0):

    View Code

    執行up_SearchAddressByNameV0,返回记录:

    共195条记录。

    客户端统计结果:

    平均的执行耗时为294.6毫秒

    方法2


    方法2是参照方法1,并借助全文索引来优化方法1中的步骤1。也就是在name列上建立全文索引,在步骤1中,通过全文索引搜索出包字段Name中含有“广”、“大”的所有地址记录存入临时表#tmp,其他步骤保持不变。

    建立全文索引:

    复制代码
    use test
    go
    if not exists(select 1 from sys.fulltext_catalogs a where a.name='ftCatalog')
    begin
    create fulltext catalog ftCatalog As default;
    end
    go
    --select * From sys.fulltext_languages
            
    create fulltext index on TBAddress(Name language 2052 ) key index PK_TBAddress
    go
    复制代码

    【注】:在Name列上创建全文索引使用的语言是简体中文(Simplified Chinese)。

    方法2脚本(up_SearchAddressByNameV1):

    View Code

    执行存储过程up_SearchAddressByNameV1,返回结果:

     

    与方法1一样返回195条记录。

    客户端统计结果:

    平均的执行耗时为180.6毫秒。

    针对方法1,方法2的优化缩短了查询时间,提高了查询性能。

    方法3


    在方法2中,我们在Name列上创建全文索引提高了查询性能,但我们不仅仅局限于一两个方法,下面我们介绍第3个方法。

    第3个方法,通过修改表的结构和创建全文索引。在表TBAddress增加多一个字段FullPath存储各个地址到Level 1的全路径,再在FullPath列上创建全文索引,然后直接通过全文索引来搜索FullPath列中包含“广”和“大”的记录。

    新增加字段FullPath,并更新列FullPath数据:

    复制代码
    use Test
    go
    if not exists(select 1 from sys.columns a where a.object_id=object_id('TBAddress') and a.name='Fullpath')
    begin
        alter table TBAddress add Fullpath nvarchar(200);
    end
    go
    
    create nonclustered index IX_TBAddress_FullPath on dbo.TBAddress(Fullpath) with(fillfactor=80,pad_index=on)
    go
    
    
    ;With cte_fullPath
        As
        (
            Select Id,Parent,LevelNo,Convert(nvarchar(500),Isnull(Name,'')) As FPath,Fullpath
                From dbo.TBAddress 
                Where LevelNo=1
            Union All
            Select A.Id,A.Parent,A.LevelNo,Convert(nvarchar(500),B.FPath+'/'+Isnull(A.Name,'')) As FPath,a.Fullpath
                From TBAddress As A
                    Inner Join cte_fullPath As B On A.Parent=B.id 
        )
        update a 
            set a.Fullpath=isnull(b.FPath,a.Name)
            from dbo.TBAddress a 
                left join cte_fullPath b on b.id=a.id
    go
    复制代码

    在列FullPath添加全文索引:

    alter fulltext index on dbo.TBAddress add(Fullpath language 2052)
    go

    方法3脚本(up_SearchAddressByNameV2):

    View Code

    执行存储过程up_SearchAddressByNameV2,结果为:

    与方法1、方法2一样返回195条记录。

    客户端统计结果:

    平均的执行耗时为41.5毫秒。

    这里我们明显可以看出,方法3查询性能比方法1、方法2都高。也就是我们想要的方法。

    当然,我们下面还列一个方法4,应用到方法3中的新增字段FullPath,直接使用Like来查询。

    方法4


    直接使用Like对列FullPath进行查询。

    方法4脚本(up_SearchAddressByNameV3):

    View Code

    执行存储过程up_SearchAddressByNameV3,结果为:

    返回195条记录。

    客户端统计结果:

    平均的执行耗时为76.2毫秒。

    虽然方法4的平均耗时比方法1、方法2都小,但从最优的角度,我们优先选择方法3.

    小结:


    在这篇我只列出在数据库中实现的4个方法,当然还有其他的方法,如通过程序实现,把数据一次性加载至内存中,再通过程序写的算法进行搜索,或通过其他工具如Lucene来实现。不管哪一种方法,我们都是选择最优的方法。实际的工作经验告诉我们,在实际应用中,多选择和测试不同的方法来,选择其中一个满足我们环境的,而且是最优的方法。

  • 相关阅读:
    matlab-vrep程序控制方法
    .ipyb文件怎么转成 .py文件
    python技巧
    gradle初步认识
    StringUtils
    Elasticsearch、Logstash和Kibana ELK
    redisTemplate、jedis、redission的对比
    唯一ID
    spring相关组件简介
    新电脑环境配置
  • 原文地址:https://www.cnblogs.com/chamge/p/2921886.html
Copyright © 2011-2022 走看看