搜索SQLite数据库数千次的最快方法?

时间:2013-08-09 08:56:54

标签: python sqlite subquery geonames

问题第一

如何尽快搜索我的SQLite数据库?

我应该解析Excel中所有60,000行的地址数据,将它们加载到列表中,然后一次只搜索所有这些行吗?

从简单地查看纯文本文件切换到我的脚本加速了3次,但我仍然认为它可以更快。

提前谢谢!


数据库

我有一个SQLite数据库,包含我从Geonames的邮政编码数据转储创建的城市名称,邮政编码,坐标等: Geonames Postal Codes

数据库有一个表格,用于表示每个国家/地区(DE,US,GB等等。总共72个),每个表格各有几十到几万行,格式如下:

country code      : iso country code, 2 characters
postal code       : varchar(20)
place name        : varchar(180)
admin name1       : 1. order subdivision (state) varchar(100)
admin code1       : 1. order subdivision (state) varchar(20)
admin name2       : 2. order subdivision (county/province) varchar(100)
admin code2       : 2. order subdivision (county/province) varchar(20)
admin name3       : 3. order subdivision (community) varchar(100)
admin code3       : 3. order subdivision (community) varchar(20)
latitude          : estimated latitude (wgs84)
longitude         : estimated longitude (wgs84)
accuracy          : accuracy of lat/lng from 1=estimated to 6=centroid

工作流

现在我在Python中的当前脚本如下:

  • 读取Excel文件中的行
  • 解析地址和位置数据(其他很多不相关的东西)
  • 在SQLite数据库中搜索匹配项
  • 将SQLite数据库中匹配行的信息写入.CSV文件

Excel文件大约有60,000行,并且每行都经过我的整个Python脚本(上面的过程)。

我的地址数据非常不一致,包含邮政编码,城市名称和国家/地区名称的混合。有时,所有这些数据都在Excel行中,有时不在。它还带有许多拼写错误和替代名称。

因为数据是如此不一致,并且因为有时人们放下了不匹配的邮政编码和城市,我目前有我的Python脚本尝试一堆不同的搜索查询,如:

  • 检查[邮政编码]是否与列完全匹配,并且[地名]与列完全匹配
  • 检查[邮政编码]是否与列
  • 完全匹配并且[地名]
  • 检查[邮政编码]是否与列完全匹配AND [地名](按字分割)列
  • 检查[邮政编码]是否与列
  • 匹配

Python脚本

以下是Python脚本的部分。如您所见,它看起来效率很低:

if has_country_code == True:
    not_in_list = False
    country = country_code.lower()+"_"
    print "HAS COUNTRY"
    if has_zipcode == True and has_city_name == True:
        print "HAS COUNTRY2"
        success = False

        try:
            curs = conn.execute("SELECT * FROM "+country+" WHERE postal_code = ? AND place_name = ? COLLATE NOCASE", (zipcode, city,))

            for row in curs:
                success = True
                break   
        except:
            not_in_list = True
            success = True

        if success != True:  
            curs = conn.execute("SELECT * FROM "+country+" WHERE postal_code = ? AND place_name LIKE ? COLLATE NOCASE", (zipcode,"%"+city+"%",))

            for row in curs:
                success = True
                break

        if success != True:
            newCity = ""  
            newCity = filter(None,re.split('[; / ( ) - ,]',city))
            questionMarks = ",".join(["?" for w in newCity])


            curs = conn.execute("SELECT * FROM "+country+" WHERE postal_code = ? AND place_name IN ("+questionMarks+") COLLATE NOCASE", ([zipcode]+newCity))

            for row in curs:
                success = True
                break   


        if success != True:
            curs = conn.execute("SELECT * FROM "+country+" WHERE postal_code = ? COLLATE NOCASE", (zipcode,))

            for row in curs:
                success = True
                break   


        if success != True:

            curs = conn.execute("SELECT * FROM "+country+" WHERE place_name = ? COLLATE NOCASE", (city,))

            for row in curs:
                success = True
                break   

        if success != True:

            curs = conn.execute("SELECT * FROM "+country+" WHERE place_name LIKE ? COLLATE NOCASE", ("%"+city+"%",))

            for row in curs:
                success = True
                break

        if success != True:
            newCity = ""  
            newCity = filter(None,re.split('[; / ( ) - ,]',city))
            questionMarks = ",".join(["?" for w in newCity])


            curs = conn.execute("SELECT * FROM "+country+" WHERE place_name IN ("+questionMarks+") COLLATE NOCASE", (newCity))

            for row in curs:
                success = True
                break

        if success != True:     
            newCity = ""                   
            newCity = filter(None,re.split('[; / ( ) - ,]',city))
            newCity.sort(key=len, reverse=True)
            newCity = (["%"+w+"%" for w in newCity])

            for item in newCity:
                curs = conn.execute("SELECT * FROM "+country+" WHERE place_name LIKE (?) COLLATE NOCASE", (item,))

                for row in curs:
                    success = True
                    break
                break   





    if has_city_name == True and has_zipcode == False:        
        try:
            curs = conn.execute("SELECT * FROM "+country+" WHERE place_name = ? COLLATE NOCASE", (city,))

            for row in curs:
                success = True
                break   
        except:
            not_in_list = True
            success = True

        if success != True:
            curs = conn.execute("SELECT * FROM "+country+" WHERE place_name LIKE ? COLLATE NOCASE", ("%"+city+"%",))

            for row in curs:
                success = True
                break

        if success != True:
            newCity = ""  
            newCity = filter(None,re.split('[; / ( ) - ,]',city))
            questionMarks = ",".join(["?" for w in newCity])


            curs = conn.execute("SELECT * FROM "+country+" WHERE place_name IN ("+questionMarks+") COLLATE NOCASE", (newCity))

            for row in curs:
                success = True
                break 



        if success != True:     
            newCity = ""                   
            newCity = filter(None,re.split('[; / ( ) - ,]',city))
            newCity.sort(key=len, reverse=True)
            newCity = (["%"+w+"%" for w in newCity])

            for item in newCity:
                curs = conn.execute("SELECT * FROM "+country+" WHERE place_name LIKE (?) COLLATE NOCASE", (item,))

                for row in curs:
                    success = True
                    break
                break   




    if has_city_name == False and has_zipcode == True:
        try:
            curs = conn.execute("SELECT * FROM "+country+" WHERE postal_code = ?", (zipcode,))

            for row in curs:
                success = True
                break

        except:
            not_in_list = True
            success = True

2 个答案:

答案 0 :(得分:2)

这可能是您可能需要尝试不同方法然后查看每个方法是否足够快的情况之一。正如@Philip所建议的索引将是一个很好的起点,如果你还没有至少邮政编码的索引,这应该会显着提高性能。

如果您已经拥有此功能或想要进一步获取,我会考虑将Excel数据加载到您的SQLite数据库中并尝试将其作为一个大查询(它需要对所有内容进行全表扫描,因为你想要获得的比赛数量,但这样做一次可能不会太糟糕。

如果没有获得您想要的结果或证明难以获得正确的查询,您可以尝试将所有SQLite数据加载到Python中并构建成字典,这些字典将根据您需要查找的内容对数据进行排序,例如每个国家/地区的一个级别的字典,每个国家/地区都有所有邮政编码,每个邮政编码都有该国家/邮政编码的所有记录列表。

基本上,这个主题是确保你正在查看散列表类型结构(排序的键值对,如数据库索引,python字典等),或者如果你确实通过记录来记录不要对其他数据集中的每条记录执行此操作。

答案 1 :(得分:1)

对于拼写错误检测,您可以查看phonetic algorithms(但每个仅针对特定语言),因为即使使用索引,LIKE '%city%'也会保持低效。

此外,您可以尝试按国家/地区重新排序Excel数据(首先询问DE,然后是美国,......)。因此,Sqlite可以“集中”在一个表上而不必一直切换,而且Python的Sqlite-wrapper的预处理语句缓存更有效。

修改

预准备语句是先前已解析和分析的SQL语句。多次执行它比始终创建和准备新语句更有效。如果再次使用完全相同的SQL语句字符串,Python的Sqlite-wrapper会缓存一些预准备语句并重用它们。