【问题标题】:Snowflake: Sqlalchemy ORM update variant fieldSnowflake:Sqlalchemy ORM 更新变体字段
【发布时间】:2021-01-03 19:22:22
【问题描述】:

我在使用 sqlalchemy 更新表示雪花表的 python 模型时遇到问题。其中一列是变体字段,当我尝试更新模型并将其保存回数据库时,雪花抱怨该值不是变体。我尝试将数据作为字典和 json 字符串传递,但都不起作用。

模型属性定义为:

data = db.Column(snowsql.VARIANT)

snowsql 是 snowflake-sqlalchemy 包的别名,该包已安装,但似乎只提供类型方言,而没有提供其他功能。

有人有这方面的经验吗?

更新:

我添加的数据是一个 python 字典列表:

new_data['data'] = [{'component': None, 'display_name': 'blah', 'key': None, 'row': {'display_name': 'blah', 'type': 'blah'}, 'sub_type': None, 'type': 'blah'}]

设置变体属性时执行 json.dumps:

new_blah = Blah(
  data = json.dumps(new_data['data'])
)

结果:

表达式类型与列数据类型不匹配,应为 VARIANT,但为 DATA 列得到 VARCHAR(8905)

转储生成的 json:

"[{\"component\":null,\"display_name\":\"blah\",\"key\":null,\"row\":{\"display_name\":\"blah\",\"type\":\"blah\"},\"sub_type\":null,\"type\":\"blah\"}]"

或者,尝试在不执行 json.dumps 的情况下分配列表:

sqlalchemy.exc.ProgrammingError: (snowflake.connector.errors.ProgrammingError) 252004: None: 处理pyformat-parameters失败; 'dict' 对象没有属性 'replace'

【问题讨论】:

  • 确保您传入的值是 JSON 而不是字符串。您能否分享您尝试传递到 Variant 字段的值?
  • 更新了数据结构。它是 python,所以我只是在做一个 json.dumps 来生成一个 json 格式的字符串。
  • 改用 json.load() 试试。我相信 json.dumps 正在把东西变成一个字符串,而你希望它是一个 json 有效负载。我好像记得以前有这个问题,所以如果 json.load() 没有帮助,我会为你挖掘代码。
  • 我无法对列表进行加载,因为它是一个 python 对象并且只能解析 json 字符串。结果是:TypeError: JSON object must be str, bytes or bytearray, not 'list'
  • 我可以确认这只是一个插入问题,如果我用完全相同的 json.dumps 更新现有行,它可以正常工作。

标签: python sqlalchemy snowflake-cloud-data-platform variant


【解决方案1】:

据我所知,您正在尝试的操作目前是不可能的。我遇到了同样的问题,尽管我对SQLAlchemy Custom Types 有很多了解,但我目前无法解决它。

问题如下:

  1. 要插入VARIANT 列,您需要使用雪花函数PARSE_JSON
  2. 要使用上述功能,您需要在SELECT 子句的上下文中,即您不能将其与INSERT ... VALUES 格式一起使用(请参阅Snowflake Community

我不知道有什么方法可以强制 SQLAlchemy 创建子查询,因为这是必需的。

如果将来这种情况发生变化,以下是我的进度:

class SnowflakeJSON(TypeDecorator):
    impl = VARIANT

    def process_bind_param(self, value, dialect):
        return json_serialize(value)

    def process_result_value(self, value, dialect):
        return json_deserialize(value)

    def bind_expression(self, bindparam):
        return func.PARSE_JSON(bindparam, type_=self)

    def copy(self, **kw):
        return SnowflakeJSON()

这会生成如下查询:

INSERT INTO my_table (key, data) 
VALUES ('001', PARSE_JSON('{"test": "hello world"}'));

但是,根据上面的雪花社区条目,VALUES 格式不起作用,您需要以下格式:

INSERT INTO my_table (key, data) 
SELECT '001', PARSE_JSON('{"test": "hello world"}');

作为备用,您始终可以使用将 JSON 对象存储为 VARCHAR 的列类型:

class SnowflakeJSON(TypeDecorator):
    impl = String

    def process_bind_param(self, value, dialect):
        return json_serialize(value)

    def process_result_value(self, value, dialect):
        return json_deserialize(value)

    def copy(self, **kw):
        return SnowflakeJSON()

这样,您显然失去了将 JSON 字符串存储为 VARIANT 数据类型的好处。但是,您可以在稍后阶段解析它,甚至在查询中:

SELECT PARSE_JSON(text)['test'] from my_table;

【讨论】:

    猜你喜欢
    • 2022-08-24
    • 1970-01-01
    • 1970-01-01
    • 2011-03-02
    • 2020-08-12
    • 2021-12-31
    • 2022-08-18
    相关资源
    最近更新 更多