2

質問ファースト

SQLite データベースをできるだけ早く検索するにはどうすればよいですか?

Excel で 60,000 行すべての住所データを解析し、それらをリストにロードしてから、一度にすべてを検索する必要がありますか?

単純にプレーン テキスト ファイルに目を通すことから切り替えることで、スクリプトが 3 倍高速化されましたが、それでももっと高速になる可能性があると思います。

前もって感謝します!


データベース

Geonames の郵便番号データ ダンプから作成した都市名、郵便番号、座標などの SQLite データベースがあります: 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 db の一致する行からの情報を .CSV ファイルに書き込みます

Excel ファイルは約 60,000 行あり、行ごとに Python スクリプト全体 (上記のプロセス) を実行します。

私の住所データは非常に一貫性がなく、郵便番号、都市名、国名が混在しています。このデータのすべてが Excel の行にある場合もあれば、そうでない場合もあります。また、多くのスペルミスや別名が付属しています。

データに一貫性がなく、一致しない郵便番号や市区町村を入力する人もいるため、現在、Python スクリプトで次のようなさまざまな検索クエリを試しています。

  • [郵便番号] が列と完全に一致し、かつ [地名] が列と完全に一致するかどうかを確認します
  • [郵便番号] が列と完全に一致し、かつ列の [地名] と一致するかどうかを確認します
  • [郵便番号] が列と完全に一致し、列の [場所名] (単語ごとに分割) が一致するかどうかを確認します
  • [郵便番号] だけが列と一致するかどうかを確認します

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
4

2 に答える 2

2

これは、さまざまなアプローチを試して、それぞれが十分に高速かどうかを確認する必要がある場合の 1 つです。@Philip が推奨するインデックスは出発点として適しているため、少なくとも郵便番号にインデックスがまだない場合は、パフォーマンスが大幅に向上するはずです。

すでにこれを持っている場合、またはさらに利益を上げたい場合は、Excel データを SQLite データベースにロードし、これを 1 つの大きなクエリとして実行することを検討します (一致の数が多いため、すべてのテーブルを完全にスキャンする必要があります)。取得しようとしていますが、これを一度実行することはそれほど悪くないかもしれません.

これで望む結果が得られない場合、またはクエリを正しく取得するのが難しいことが証明されている場合は、すべての SQLite データを Python にロードして、検索する必要があるものでデータを並べ替える辞書を作成してみてください。たとえば、1 レベルの辞書などです。国の場合、各国にはすべての郵便番号があり、各郵便番号にはその国/郵便番号のすべてのレコードのリストがあります。

基本的に、これのテーマは、ハッシュ テーブル タイプの構造 (データベース インデックス、python 辞書などの並べ替えられたキーと値のペア) に対してルックアップを行っていることを確認することです。これは、他のデータセットの各レコードに対して。

于 2013-08-09T09:51:50.807 に答える
1

スペルミスの検出については、音声アルゴリズムを調べることができます(ただし、それぞれが特定の言語専用に作成されています) LIKE '%city%'。これは、インデックスを使用しても特に効率が悪いためです。

さらに、Excel データを国別に並べ替えることもできます (最初に DE を要求し、次に US を要求します...)。したがって、Sqlite は 1 つのテーブルに「集中」でき、常に切り替える必要はありません。さらに、Python の Sqlite-wrapper の準備済みステートメント キャッシュはより効率的に機能します。

編集:

準備済みステートメントは、以前に解析および分析された SQL ステートメントです。常に新しいステートメントを作成して準備するよりも、複数回実行する方が効率的です。Python の Sqlite-wrapper は、いくつかの準備済みステートメントをキャッシュし、まったく同じ SQL ステートメント文字列が再度使用された場合にそれらを再利用します。

于 2013-08-09T09:44:16.383 に答える