我的代码
from psycopg2.extras import Json
from psycopg2.extensions import register_adapter
register_adapter(dict, Json)
data = [{
'end_of_epoch_data': ['GasCoin', [{'Input': 5}, {'Input': 6}, {'Input': 7}]],
}]
def get_upsert_sql(schema: str, table: str, columns: str, primary_keys: list | tuple | set):
return f"""INSERT INTO {schema}.{table}
({', '.join(columns)}) VALUES %s
ON CONFLICT ({','.join(primary_keys)}) DO UPDATE
SET {', '.join([f"{col}=EXCLUDED.{col}" for col in columns if col not in primary_keys])}"""
def upsert(data: list, uri: str, schema: str, table: str, primary_keys: list | tuple | set):
connection = psycopg2.connect(uri)
cursor = connection.cursor()
try:
columns = data[0].keys()
query = get_upsert_sql(schema, table, columns, primary_keys)
values = [[d[col] for col in columns] for d in data]
execute_values(cursor, query, values)
connection.commit()
except Exception as e:
connection.rollback()
raise e
finally:
cursor.close()
connection.close()
但是我遇到了类似的错误
File "/Users/tests/test_pg_write.py", line 47, in upsert
execute_values(cursor, query, values)
File "/Users/venv/lib/python3.9/site-packages/psycopg2/extras.py", line 1299, in execute_values
cur.execute(b''.join(parts))
psycopg2.errors.InvalidTextRepresentation: malformed array literal: "GasCoin"
LINE 2: (end_of_epoch_data) VALUES (ARRAY['GasCoin',ARRA...
^
DETAIL: Array value must start with "{" or dimension information.
end_of_epoch_data
是postgres表中的jsonb列
有什么想法吗?谢谢
更新
该错误似乎是因为我尝试将 python 列表写入 pg 表中的 jsonb 列。但似乎我可以将
json.dumps(dataend_of_epoch_data)
(python 列表的 str)写入 postgres 表的 jsonb 列...这是正确的解决方案吗?
不要将数据变成列表的列表,而是按原样编写:
#values = [[d[col] for col in columns] for d in data] #no
values = json.dumps(data) #yes
如果
end_of_epoch_data
被声明为列类型 text
、json
或 jsonb
PostgreSQL 将接受它作为有效的文字。如果您尝试编写像您拥有的那样的列表列表,PostgreSQL 不会接受它无论您将其保存到哪种类型的列。
您收到错误的原因是 PostgreSQL 数组要求:
多维数组的每个维度必须具有匹配的范围。不匹配会导致错误,例如:
这个:
values = [[d[col] for col in columns] for d in data]
print(values[0])
#[['GasCoin', [{'Input': 5}, {'Input': 6}, {'Input': 7}]]]
获取Python数组(列表),这在Python中ok:
my_python_list_of_lists = ['1dim',['2dim',['3dim']]]
print(type(my_python_list_of_lists),my_python_list_of_lists)
#<class 'list'> ['1dim', ['2dim', ['3dim']]]
但是在 PostgreSQL 中不起作用,因为上面的要求:
create table test as select array['1dim',array['2dim',array['3dim']]];
ERROR: malformed array literal: "2dim" LINE 1: create table test as select array['1dim',array['2dim',array[... ^ DETAIL: Array value must start with "{" or dimension information.
第一个元素是一个平面、非数组、标量
text
。第二个元素是一个数组(在你的例子中,是对象/字典的数组,但 PostgreSQL 并没有做到这一点)。当您将其提供给 PostgreSQL 时,它会读取第一个元素,并认为整个传入的内容可能是一个简单的 text[]
,一维 text
数组。然后它继续到第二个元素,结果明确是ARRAY[]
。为了保持这一点,该值必须是 text[][]
,而不仅仅是 text[]
。
要使其发挥作用,第一个值也必须是
ARRAY[]
- 或可强制转换为 - 以获得匹配范围数组。 这是一个可以发挥作用的示例:
create table test
as (values (array['{a,b,c}',--this is `text`, but it could also work as `text[]`
array['d','e','f']
]
)
);
第1栏 | 数组尺寸 |
---|---|
{{a,b,c},{d,e,f}} | [1:2][1:3] |
第一个
text
元素结果是一个有效的数组文字,因此它被解释为这样,到达一个 2x3 数组。
这些也行不通,因为即使元素是可强制的,嵌套数组也不相等:
create table test as (values (array['{a,b,c,d}',array['d','e','f']]));
select column1,array_dims(column1) from test;
ERROR: multidimensional arrays must have array expressions with matching dimensions
create table test as (values (array['{a,b,c}',array['d','e','f','g']]));
select column1,array_dims(column1) from test;
ERROR: multidimensional arrays must have array expressions with matching dimensions