通过 sqlalchemy 映射类在 MySQL JSON 字段中插入键而不进行查询

Posted

技术标签:

【中文标题】通过 sqlalchemy 映射类在 MySQL JSON 字段中插入键而不进行查询【英文标题】:Insert key in a MySQL JSON field through sqlachemy mapped class without doing a query 【发布时间】:2020-02-07 23:31:12 【问题描述】:

让我们从定义我的映射类开始

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.types import JSON
from sqlalchemy.ext.mutable import MutableDict
from sqlalchemy import Column, Integer

base = declarative_base()

class MinimalExample(Base):
    __tablename__ = 'users'
    id = Column(Integer)
    data = Column(MutableDict.as_mutable(JSON))

假设我的表中有一行 id=1data='key_0': 1

我可以很容易地通过session 对象请求它

first_row = session.query(MinimalExample)\
                   .filter(MinimalExample.id==1)\
                   .first()

我可以从那里以任何我想要的方式修改 JSON 字段并更新数据库:

first_row.data['key_2'] = 1
session.commit()

上面的代码有效(因为我们将 JSON 字段标记为可变字典,我们不必手动将其标记为脏)

session.query(MinimalExample.data)\
                   .filter(MinimalExample.id==1)\
                   .first()

>>> 'key_0': 1, 'key_2': 1

但这意味着我必须在本地计算机上查询我想要修改的所有行,这太慢了/我没有足够的内存。

我正在寻找一种方法来使用sqlalchemy.update 函数来获得相同的结果

这是我已经尝试过的:

from sqlalchemy import update

stmt = update(MinimalExample)
           .where(MinimalExample.id=1)\
           .values(MinimalExample.data='key_1': 1)

session.execute(stmt)
session.commit()

这只是用字典 'key_1': 1 替换我行中的 data 字段

类似的事情似乎是要走的路:

(编辑:找到了一种方法来更正我以前的代码,因此它不再引发错误,只需将对象放入字典中)

stmt = update(MinimalExample)
           .where(MinimalExample.id=1)\
           .values(MinimalExample.data['key_1']:1)

看这个语句的字符串好像还不错

str(stmt)

>>> 'UPDATE users SET data[:data_1]=:param_1 WHERE users.id = :id_1'

但是执行此语句会导致 ProgrammingError: (mysqldb._exceptions.ProgrammingError) (1064, 'You have an error in your SQL syntax...

对此有什么想法吗?如果我可以同时更新多行,则可以加分

【问题讨论】:

【参考方案1】:

要更新 MySQL 中的 JSON 列,您需要使用 JSON_SET() 函数(有关详细信息,请参阅 here),例如,工作查询如下所示:

UPDATE users SET data = JSON_SET(data, "$.key_1", 1) WHERE id = 1;

在 SQLAlchemy 中进行模拟:

qry = (
    update(MinimalExample)
    .where(MinimalExample.id == 1)
    .values(
        "data": func.JSON_SET(MinimalExample.data, "$.key_1", 1)
    )
)
s.execute(qry)
s.commit()

这是从创建表到提交的日志,您可以看到没有发出 SELECT:

2019-10-11 09:37:01,672 INFO sqlalchemy.engine.base.Engine
CREATE TABLE users (
        id INTEGER NOT NULL AUTO_INCREMENT,
        data JSON,
        PRIMARY KEY (id)
)


2019-10-11 09:37:01,684 INFO sqlalchemy.engine.base.Engine 
2019-10-11 09:37:01,708 INFO sqlalchemy.engine.base.Engine COMMIT
2019-10-11 09:37:01,716 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2019-10-11 09:37:01,718 INFO sqlalchemy.engine.base.Engine INSERT INTO users (id, data) VALUES (%(id)s, %(data)s)
2019-10-11 09:37:01,718 INFO sqlalchemy.engine.base.Engine 'id': 1, 'data': '"key_0": 1'
2019-10-11 09:37:01,720 INFO sqlalchemy.engine.base.Engine COMMIT
2019-10-11 09:37:01,723 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2019-10-11 09:37:01,724 INFO sqlalchemy.engine.base.Engine UPDATE users SET data = JSON_SET(data, "$.key_1", 1) WHERE id = 1;
2019-10-11 09:37:01,724 INFO sqlalchemy.engine.base.Engine 
2019-10-11 09:37:01,725 INFO sqlalchemy.engine.base.Engine COMMIT

【讨论】:

对我来说很好用,谢谢!目前,我正在遍历所有要更新的行以执行此查询,但这很慢。您是否知道如何使用此查询一次更新多行(使用不同的值)?

以上是关于通过 sqlalchemy 映射类在 MySQL JSON 字段中插入键而不进行查询的主要内容,如果未能解决你的问题,请参考以下文章

MySQL表结构映射为Python中的对象 python (SQLAlchemy)

pandas读取大文件(chunksize)并通过sqlalchemy写入MySQL数据库

Mysql:ORM框架SQLAlchemy

MySQL之ORM框架SQLAlchemy

mysql八:ORM框架SQLAlchemy

mysql八:ORM框架SQLAlchemy