今天遇到一个情况是对通过SQLAlchemy查询出来的对象更新其JSON字段,没有效果,百思不得其解,后来Google出来了原因,原来是Postgres的问题,并且引出了JSON和JSONB的概念。。

解决方案在这里

总结一下,有两种办法:

使用 flag_modified 标记

标记之后SQLAlchemy才能知道这个字段改变了。

1
2
3
4
5
config = session.query(Config).filter(Config.id==1).one()
from sqlalchemy.orm.attributes import flag_modified
config.values["do_something"] = false
flag_modified(config, "values")
session.commit()

这种方法比较好用

直接update

1
2
3
value["do_something"] = false
session.query(Config).filter(Config.id == 1).update({"values": values})
session.commit()

JSON和JSONB

在 PostgreSQL 9.5 添加了增量更新的功能,但必须使用 JSONB 类型。在本质上,JSON类型是以文本形式存储的,而JSONB是预处理成二进制格式然后在内部存储。

这样导致:

  • jsonb通常比json占用更多的磁盘空间(有些情况不是)
  • jsonb比json的写入更耗时间
  • json的操作比jsonb的操作明显更耗时间(在操作一个json类型值时需要每次都去解析)

如何选择:

  • 如果你的应用只用json表示,PostgreSQL只用于保存与获取时,你应该使用json.
  • 如果你需要在PostgreSQL中做比较多的json值的操作,或者在一些json字段上使用索引时,你应该使用jsonb

官方文档上说:

有两个JSON数据类型:json和jsonb。它们接受几乎相同的值作为输入。它们实际的主要差别是效率。json 数据类型存储输入文本的精确拷贝,处理函数必须在每个执行上重新解析;而jsonb数据以分解的二进制格式存储,这使得它由于添加了转换机制而在输入上稍微慢些,但是在处理上明显更快,因为不需要重新解析。jsonb也支持索引,这也是一个明显的优势。

因为json类型存储输入文本的精确拷贝,它将保存语义上无关紧要的空格和JSON对象中键的顺序。另外,如果值中的一个JSON对象多次包含相同的键,那么保存所有的键/值对。(处理函数将最后一个值当做操作值。)相比之下, jsonb不保存空格,也不保存对象键的顺序,并且不保存重复对象键。如果在输入中指定了重复的键,那么只保存最后一个值。