2017-01-04 55 views
5

Podczas procesu ETL potrzebowałem wyodrębnić i załadować kolumnę JSON z jednej bazy danych Postgres do drugiej. Używamy do tego Pand, ponieważ ma wiele sposobów na odczytywanie i zapisywanie danych z różnych źródeł/miejsc docelowych, a wszystkie transformacje można zapisywać za pomocą Pythona i Pandy. Jesteśmy szczęśliwi, że podejście to jest szczere ... ale trafiliśmy na problem.Zapisywanie kolumny JSON do Postgres przy użyciu Pand. To_sql

Zwykle dość łatwo jest odczytać i zapisać dane. Wystarczy użyć pandas.read_sql_table, aby odczytać dane ze źródła i pandas.to_sql, aby zapisać je do miejsca docelowego. Ale ponieważ jedna z tabel źródłowych miała kolumnę typu JSON (z Postgres), funkcja to_sql uległa awarii z następującym komunikatem o błędzie.

df.to_sql(table_name, analytics_db) 
    File "/home/ec2-user/python-virtual-environments/etl/local/lib64/python2.7/site-packages/pandas/core/generic.py", line 1201, in to_sql 
    chunksize=chunksize, dtype=dtype) 
    File "/home/ec2-user/python-virtual-environments/etl/local/lib64/python2.7/site-packages/pandas/io/sql.py", line 470, in to_sql 
    chunksize=chunksize, dtype=dtype) 
    File "/home/ec2-user/python-virtual-environments/etl/local/lib64/python2.7/site-packages/pandas/io/sql.py", line 1147, in to_sql 
    table.insert(chunksize) 
    File "/home/ec2-user/python-virtual-environments/etl/local/lib64/python2.7/site-packages/pandas/io/sql.py", line 663, in insert 
    self._execute_insert(conn, keys, chunk_iter) 
    File "/home/ec2-user/python-virtual-environments/etl/local/lib64/python2.7/site-packages/pandas/io/sql.py", line 638, in _execute_insert 
    conn.execute(self.insert_statement(), data) 
    File "/home/ec2-user/python-virtual-environments/etl/local/lib64/python2.7/site-packages/sqlalchemy/engine/base.py", line 945, in execute 
    return meth(self, multiparams, params) 
    File "/home/ec2-user/python-virtual-environments/etl/local/lib64/python2.7/site-packages/sqlalchemy/sql/elements.py", line 263, in _execute_on_connection 
    return connection._execute_clauseelement(self, multiparams, params) 
    File "/home/ec2-user/python-virtual-environments/etl/local/lib64/python2.7/site-packages/sqlalchemy/engine/base.py", line 1053, in _execute_clauseelement 
    compiled_sql, distilled_params 
    File "/home/ec2-user/python-virtual-environments/etl/local/lib64/python2.7/site-packages/sqlalchemy/engine/base.py", line 1189, in _execute_context 
    context) 
    File "/home/ec2-user/python-virtual-environments/etl/local/lib64/python2.7/site-packages/sqlalchemy/engine/base.py", line 1393, in _handle_dbapi_exception 
    exc_info 
    File "/home/ec2-user/python-virtual-environments/etl/local/lib64/python2.7/site-packages/sqlalchemy/util/compat.py", line 202, in raise_from_cause 
    reraise(type(exception), exception, tb=exc_tb, cause=cause) 
    File "/home/ec2-user/python-virtual-environments/etl/local/lib64/python2.7/site-packages/sqlalchemy/engine/base.py", line 1159, in _execute_context 
    context) 
    File "/home/ec2-user/python-virtual-environments/etl/local/lib64/python2.7/site-packages/sqlalchemy/engine/default.py", line 459, in do_executemany 
    cursor.executemany(statement, parameters) 
sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) can't adapt type 'dict' 

Odpowiedz

8

Szukałem w internecie rozwiązania, ale nie mógł znaleźć żadnego więc tutaj jest to, co wymyślił (nie może być lepsze sposoby, ale przynajmniej jest to początek, jeśli ktoś prowadzi do tego).

Podaj parametr dtype w to_sql.

Udaliśmy się od: df.to_sql(table_name, analytics_db) do df.to_sql(table_name, analytics_db, dtype={'name_of_json_column_in_source_table': sqlalchemy.types.JSON}) i to po prostu działa.