LoginSignup
0
0

【Golang】SQLite3 でテーブル名一覧と DB の総レコード数を取得する

Last updated at Posted at 2022-08-15

Go 言語(以下 Golang)の database/sql 標準パッケージと、SQLite3 ドライバで DB を作成したものの、動的に作成されたテーブルのため、作成されたテーブル名の一覧を取得する必要がある

しかも、何件登録されているか、DB 内の総レコード数を後からでも確認する方法がないものか。

Go 1.18 以降、ドライバーは、もちろん天下の mattn さんの "go-sqlite3"

「"golang" "sqlite3" テーブル名 一覧 取得」でググっても、SQLite コマンドの .table で確認する方法しか出てこなかったり、「"golang" "sqlite3" 総レコード数」でググっても、データベース内の全件のレコード数が欲しいのに COUNT() を使った特定のテーブルのレコード数しか情報がなかったので、自分のググラビリティとして。

TL; DR (今北産業)

  1. SQLite3 では count() を呼び出すたびにテーブルのフルスキャンを行うため遅いです。
  2. SQLite3 は仕様によりテーブルのレコード数を保持していません。キャッシュもしません。
  3. そのため、レコード数を保持するカウント用のテーブルを作成し更新するのが現実的です。
    • 更新するパターン
      1. すべてのレコードを挿入し終わった後に、カウントテーブルを更新する。
      2. INSERT DELETEトリガーを設定しておき、随時カウントテーブルを増減更新する。(全体のパフォーマンスに影響がでます)
    • 更新方法
      1. WHERE type='table' でテーブル名一覧を取得する。
      2. 各々のテーブルで SELECT count() によりレコード数を取得する。

TS; DR (サンプル)

  1. テーブル名一覧を取得し、各々のレコード数を取得する SQL クエリ (for SQLite3)

    テーブル名一覧を取得するSQLクエリ
    SELECT name FROM sqlite_master WHERE type = 'table';
    
    各々のテーブルのレコード数を取得するSQLクエリ
    SELECT 'SELECT count(*), "' || name || '" FROM ' || name || ';'  FROM sqlite_master WHERE type = 'table';
    
  2. Golang でテーブル名一覧を取得するサンプル。

    // GetTable は db のテーブル名一覧を文字列のスライスで返します。
    func GetTable(db *sql.DB) ([]string, error){
        result := []string{}
        q := `SELECT name FROM sqlite_master WHERE type = 'table';`
    
        res, err := db.Query(q)
        if err != nil {
            return nil, errors.Wrap(err, "failed to query database")
        }
    
        var table string
    
        for res.Next() {
            if err := res.Scan(&table); err != nil {
                return nil, errors.Wrap(err, "failed to scan table name")
            }
    
            result = append(result, table)
        }
    
        sort.Slice(result, func(i, j int) bool {
            return result[i] < result[j]
        })
    
        return result, nil
    }
    
  3. Golang でデータベース内(全テーブル)の総レコード数を取得するサンプル。

    func GetTotalRecords(db *sql.DB) int {
        talbes, err := Tables(db)
        if err != nil {
            return -1
        }
    
        const queryTPL = `SELECT COUNT(*) FROM %s;`
    
        result := int(0)
    
        for _, table := range talbes {
            queryTMP := fmt.Sprintf(queryTPL, table)
    
            // ここはPrepareでステートメント化した方が速い
            res, err := k.db.Query(queryTMP)
            if err != nil {
                return -1
            }
    
            if res.Next() {
                var count int
    
                res.Scan(&count)
                result += count
            }
        }
    
        return result
    }
    
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0