問題
CSVファイルからデータをインポートするときにNULLを入れようとすると怒られる。空文字列、null、NULLでも同じ。
環境
Windows10
PostgreSQL14
Python 3.10.0
psycopg2 2.9.2
テーブル作成
create table test (id varchar(10), time time);
CSVインポート
ちなみにio.StringIO(文字列)で、open関数と同じようなことがでるらしい。
公式Document:class io.StringIO
実際にCSVファイルをインポートするときはopen("/path/to/file.csv", "r")などとする。
import psycopg2
import io
input = "01,12:34\n02,17:55\n03,"
with psycopg2.connect("dbname=test host=localhost user=test password=test") as conn:
with conn.cursor() as cur:
with io.StringIO(input) as f:
cur.copy_from(f, table="test", sep=",")
こんな感じで登録したい。
id | time |
---|---|
01 | 12:34 |
02 | 17:55 |
03 | NULL |
エラー内容
> python csvimport.py
Traceback (most recent call last):
File "C:\***\csvimport.py", line 9, in <module>
cur.copy_from(f, table="test", sep=",")
psycopg2.errors.InvalidDatetimeFormat: time型の入力構文が不正です: ""
CONTEXT: testのCOPY、行 3、列 time: ""
id=03のところのtime(空文字列部分)をNULLやnullにしても同じ。
psycopg2.errors.InvalidDatetimeFormat: time型の入力構文が不正です: "NULL"
CONTEXT: testのCOPY、行 3、列 time: "NULL"
psycopg2.errors.InvalidDatetimeFormat: time型の入力構文が不正です: "null"
CONTEXT: testのCOPY、行 3、列 time: "null"
原因
copy_from関数でnullが、デフォルトでは\Nっていう値に設定されている。
公式Document:The cursor class
解決方法
copy_fromを呼ぶときにnull="NULL"などとして、CSVのNULLを入れたいところにNULLと書けばよい。
import psycopg2
import io
input = "01,12:34\n02,17:55\n03,NULL" # NULLを入れたいところにNULLと書く
with psycopg2.connect("dbname=test host=localhost user=test password=test") as conn:
with conn.cursor() as cur:
with io.StringIO(input) as f:
cur.copy_from(f, table="test", sep=",", null="NULL") # 文字列"NULL"をNULLとしてコピー
結果
修正後のcsvimport.pyを実行したらNULLを含めて登録された。
test=> select * from test;
id | time
----+----------
01 | 12:34:00
02 | 17:55:00
03 |
(3 行)
まとめ
ドキュメントを読もう、という話ではあるんだけど、言い訳をすると、原因がどこかが分かるまではドキュメントにも行きつかないと思うんですよ。空文字とかNULLって書いたらNULLが入ると思うじゃないですか。(震え声)
環境によって何をNULLとするかは変わるっていう知見が得られた。