使用 JSON1 更新 SQLite 中的 JSON

Dro*_*dOS 8 sqlite json sql-update sqlite-json1

SQLite JSON1 扩展有一些非常巧妙的功能。但是,我无法弄清楚如何更新或插入单个 JSON 属性值。

这是一个例子

CREATE TABLE keywords
(
 id INTEGER PRIMARY KEY,
 lang INTEGER NOT NULL,
 kwd TEXT NOT NULL,
 locs TEXT NOT NULL DEFAULT '{}'
);

CREATE INDEX  kwd ON keywords(lang,kwd);
Run Code Online (Sandbox Code Playgroud)

我使用此表来存储关键字搜索并记录对象中启动搜索的位置locs。此数据库表中的示例条目如下所示

 id:1,lang:1,kwd:'stackoverflow',locs:'{"1":1,"2":1,"5":1}'
Run Code Online (Sandbox Code Playgroud)

这里的位置对象属性是存储在其他地方的实际位置的索引。

现在想象一下以下场景

  • stackoverflow从位置索引“2”开始搜索。在这种情况下,我只想增加该索引处的值,以便在操作之后相应的行读取

    id:1,lang:1,kwd:'stackoverflow',locs:'{"1":1,"2": 2 ,"5":1}'

  • 从先前未知的位置索引“7”开始搜索stackoverflow,在这种情况下,更新后的相应行必须读取

    id:1,lang:1,kwd:'stackoverflow',locs:'{"1":1,"2":1,"5":1, "7":1 }'

我不清楚这实际上是否可以做到。我尝试了一些类似的事情

UPDATE keywords json_set(locs,'$.2','2') WHERE kwd = 'stackoverflow';
Run Code Online (Sandbox Code Playgroud)

这给出了错误消息error near json_set。我非常感谢任何能够告诉我如何/是否应该/可以完成此操作的人。

小智 7

没有必要创建如此复杂的带有子查询的 SQL 来执行此操作。

下面的 SQL 可以满足您的需求。

UPDATE keywords 
SET locs = json_set(locs,'$.7', IFNULL(json_extract(locs, '$.7'), 0) + 1) 
WHERE kwd = 'stackoverflow';
Run Code Online (Sandbox Code Playgroud)

我知道这很旧,但它就像搜索时的第一个链接,它值得一个更好的解决方案。


Dro*_*dOS 4

我本可以删除这个问题,但考虑到 SQLite JSON1 扩展似乎相对不太被理解,我觉得在这里提供一个答案会更有用,以造福于其他人。我在这里打算做的事情是可能的,但 SQL 语法相当复杂。

 UPDATE keywords set  locs = 
 (select json_set(json(keywords.locs),'$.**N**', 
 ifnull(
 (select json_extract(keywords.locs,'$.**N**') from keywords where id = '1'),
 0) 
 + 1) 
 from keywords where id = '1') 
 where id = '1';
Run Code Online (Sandbox Code Playgroud)

将完成我在上面的原始问题中描述的两项更新。考虑到这看起来有多复杂,有一些解释是有序的

  • UPDATE keywords部分进行实际更新,但它需要知道要更新什么
  • SELECT json_set部分是我们建立要更新的值的地方
  • 如果相关值首先不存在,我们不想对+ 1空值执行操作,因此我们进行IFNULL测试
  • 这些WHERE id =位确保我们瞄准正确的行

现在在 SQLite 中使用 JSON1 一段时间后,我有一个技巧可以与其他走同样道路的人分享。为了执行就地 JSON 操作,很容易浪费时间编写极其复​​杂且难以维护的 SQL。考虑在内存表中使用 SQLite -CREATE TEMP TABLE...存储中间结果并编写一系列 SQL 语句。这使得代码更容易理解和维护。