excute_values 到 jsonb 列时,数组值必须以“{”或维度信息开头

问题描述 投票:0回答:1

我的代码

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 列...这是正确的解决方案吗?

python postgresql psycopg2
1个回答
0
投票

不要将数据变成列表的列表,而是按原样编写:

#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
© www.soinside.com 2019 - 2024. All rights reserved.