やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

SQLite3でTSVをインポートするときにパスを渡せない問題

外部から呼び出すときにファイル参照できなくなる。

成果物

GitHubPython.dataset.SQLite3.Tsv2Insert.201703211257

開発環境

問題の概要

SQLite3でTSVをインポートするときにパスを渡せないせいで外部から呼び出した時に動作しないことがある。

ちょっと説明が面倒でわかりにくい。ようするに.sqlファイル内でパスを動的にできないせいで、相対パスが思うようにならない。tsvファイルを参照するときに参照できなくなってしまう。.importコマンド実行時に困る。

.sql内でシェルスクリプトのように現在ファイルパスやディレクトリパスを取得できたらいいのだが、シェルコマンドが使えない。引数も受け取れない。固定パスしか使えない。

絶対パスにすればいいのだが、バックアップなどで異なるパスになった場合に動作しなくなるため困る。

正常時

SQLite3でtsvをインポートするとき、以下のような構成で、以下のように実行できる。

bash Run.sh
ファイル名 説明
Run.sh SQLite3ファイルにTSVファイルのデータをインポートする。
TestDB.sqlite3 対象DBファイル
insert.sql SQLite3のimportコマンド
SomeTable.tsv SomeTableテーブルに挿入するデータ

Run.sh

sqlite3 ./TestDB.sqlite3 < ./insert.sql

DB

項目 名前
DBファイル TestDB.sqlite3
テーブル SomeTable

挿入

insert.sql

.mode tabs
.import ./insert.tsv SomeTable

insert.tsv

Column1 Column2
r1c1    r1c2
r2c1    r2c2
r3c1    r3c2

Pythonで呼び出す

python3 Main.py

Main.py

import subprocess
import shlex
subprocess.call(shlex.split("bash ./Run.sh"))
ファイル名 説明
Run.py SQLite3ファイルにTSVファイルのデータをインポートする。
Run.sh SQLite3ファイルにTSVファイルのデータをインポートする。
TestDB.sqlite3 対象DBファイル
insert.sql SQLite3のimportコマンド
SomeTable.tsv SomeTableテーブルに挿入するデータ

問題

insert.sqlファイル内にある./insert.tsvが参照できなくなる場合がある。

insert.sql

.mode tabs
.import ./insert.tsv SomeTable

以下のような構成にしておく。

  • /test/
    • Main.py
      • create
        • Run.sh
        • TestDB.sqlite3
        • insert.sql
        • SomeTable.tsv

以下のように実行する。

python3 Main.py

カレントディレクトリは/test/である。

このとき、先述のinsert.sql内の./insert.tsv/test/insert.tsvと解釈される。しかし実際には/test/create/insert.tsv`にある。よって参照できない。

使えない解決案

  • sqlite3コマンドに.tabs .import ./insert.tsv SomeTableコマンドを渡す
    • SQLite3では.sqlファイルパスを渡すことしかできない
  • insert.sqlに引数を渡す
    • SQLite3には.sqlファイルに引数を渡す機能がない
  • Run.shで.importコマンドを実行する
    • .importはSQLite3文脈でないと実行できない
      • .tabs .import ./insert.tsv SomeTableの処理内容を.shファイル内で表現できればパスの問題は解決できただろうに…
  • insert.sqlファイル内でdirname $0などのコマンドを使う
    • .sqlファイル内ではシェルコマンドが使えない

SQLite3だけでは不可能。事実上、今回のように.sqlから外部ファイル(.tsv)を参照する場合、外部から呼び出せない。

何か方法があるのかもしれないが、知らないし思いつかない。

解決案

pythonでツールを作る。

tsv2insert.py "{TSVファイルパス}" "{SQLite3ファイルパス}" "テーブル名"

SomeTable.tsv

{1行目は列名を書いたヘッダ行}
{2行目以降はデータ行}

datasetを使う。

所感

何か方法があるはずだとは思うのだが知らない。

前々から疑問だったし、使いづらかった。仕方ないからツールを作った。