簡體   English   中英

MySQL 5.7+,嵌套路徑中的 JSON_SET 值

[英]MySQL 5.7+, JSON_SET value in nested path

對於最近的開發項目,我們使用 MySQL 5.7,因此我們可以利用最新的 JSON 函數......

我正在構建一個 UPDATE 查詢,其中嵌套的 json 對象應插入/添加到 JSON 類型的屬性列中,請參閱下面的查詢。

UPDATE `table` SET `table`.`name` = 'Test',
    `table`.`attributes` = JSON_SET(
         `table`.`attributes`,
         "$.test1", "Test 1",
         "$.test2.test3", "Test 3"
     )

當我執行此查詢時,屬性字段包含數據

{"test1": "Test 1"} 

而不是通緝

{"test1", "Test 1", "test2": {"test3", "Test 3"}}

還嘗試使用 JSON_MERGE,但是當我多次執行它時,它會創建一個 JSON 對象,例如

{"test1": ["Test 1", "Test 1", "Test 1"... etc.], "test2": {"test3": ["Test 3", "Test 3", "Test 3"... etc.]}}

那么,當節點不存在時 JSON_SET 不起作用? JSON_MERGE 合並到無窮大?

JSON 對象中使用的鍵可以由用戶定義,因此不可能為所有可能的鍵創建空的 JSON 對象。 我們真的需要在每個 UPDATE 查詢之前執行 JSON_CONTAINS / JSON_CONTAINS_PATH 查詢以確定我們是否需要使用 JSON_SET 或 JSON_MERGE / JSON_APPEND 嗎?

我們正在尋找一種始終有效的查詢方法,因此當給出"$.test4.test5.test6" ,它將擴展當前的 JSON 對象,添加完整路徑......如何做到這一點?

從 MySQL 5.7.13 版開始,假設您希望得到最終結果

{"test1": "Test 1", "test2": {"test3": "Test 3"}}

在您的示例中,正在更新的attributes列設置為{"test1": "Test 1"}

查看您的初始UPDATE查詢,我們可以看到$.test2.test3不存在。 所以不能設置為

JSON_SET() 在 JSON 文檔中插入或更新數據並返回結果。 如果任何參數為 NULL 或路徑(如果給定)未定位對象,則返回 NULL。

含義 MySQL 可以添加$.test2 ,但由於$.test2不是對象,MySQL 無法添加到$.test2.test3

因此,您需要通過執行以下操作將$.test2定義為 json 對象。

mysql> SELECT * FROM testing;
+----+---------------------+
| id | attributes          |
+----+---------------------+
|  1 | {"test1": "Test 1"} |
+----+---------------------+
1 row in set (0.00 sec)
mysql> UPDATE testing
    -> SET attributes = JSON_SET(
    ->     attributes,
    ->     "$.test1", "Test 1",
    ->     "$.test2", JSON_OBJECT("test3", "Test 3")
    -> );
Query OK, 1 row affected (0.03 sec)
Rows matched: 1  Changed: 1  Warnings: 0
mysql> SELECT * FROM testing;
+----+---------------------------------------------------+
| id | attributes                                        |
+----+---------------------------------------------------+
|  1 | {"test1": "Test 1", "test2": {"test3": "Test 3"}} |
+----+---------------------------------------------------+
1 row in set (0.00 sec)

因此,您不需要依賴 MySQL 點符號,而是需要明確告訴 MySQL 該鍵作為 JSON 對象存在。

這類似於 PHP 如何定義不存在的對象屬性值。

$a = (object) ['test1' => 'Test 1'];
$a->test2->test3 = 'Test 3';

//PHP Warning:  Creating default object from empty value

要消除錯誤,您需要首先將$a->test2定義為一個對象。

$a = (object) ['test1' => 'Test 1'];
$a->test2 = (object) ['test3' => 'Test 3'];

或者,您可以在使用點表示法之前測試和創建對象,以設置值。 盡管對於較大的數據集,這可能是不可取的。

mysql> UPDATE testing
    -> SET attributes = JSON_SET(
    ->     attributes, "$.test2", IFNULL(attributes->'$.test2', JSON_OBJECT())
    -> ),
    -> attributes = JSON_SET(
    ->     attributes, "$.test4", IFNULL(attributes->'$.test4', JSON_OBJECT())
    -> ),
    -> attributes = JSON_SET(
    ->     attributes, "$.test4.test5", IFNULL(attributes->'$.test4.test5', JSON_OBJECT())
    -> ),
    -> attributes = JSON_SET(
    ->     attributes, "$.test2.test3", "Test 3"
    -> );
Query OK, 1 row affected (0.02 sec)
Rows matched: 1  Changed: 1  Warnings: 0
mysql> SELECT * FROM testing;
+----+---------------------------------------------------------------------------+
| id | attributes                                                                |
+----+---------------------------------------------------------------------------+
|  1 | {"test1": "Test 1", "test2": {"test3": "Test 3"}, "test4": {"test5": {}}} |
+----+---------------------------------------------------------------------------+
1 row in set (0.00 sec)

盡管在任何一種情況下,如果未提供原始數據,JSON_OBJECT 函數調用都會清空嵌套對象的屬性值。 但是正如您從上一個JSON_SET查詢中看到的, $.test1沒有在attributes的定義中提供,並且保持不變,因此可以從查詢中省略那些未修改的屬性。

現在,從 MySQL 5.7.22 版本開始,最簡單的方法是像這樣使用JSON_MERGE_PATCH

UPDATE `table` SET `attributes` = 
JSON_MERGE_PATCH(`attributes`, '{"test2": {"test3": "Test 3"}, "test4": {"test5": {}}}')

這給出了{"test1": "Test 1", "test2": {"test3": "Test 3"}, "test4": {"test5": {}}}的預期結果,如您的示例所示。

Fyrye,感謝 awnser,非常感謝! 由於數據沒有固定的結構,並且每條記錄都可能不同,我需要一個解決方案,我可以在其中生成一個查詢,該查詢將在單個查詢中自動生成總 JSON 對象。

我真的很喜歡你使用JSON_SET(attributes, "$.test2", IFNULL(attributes->'$.test2',JSON_OBJECT()))方法的解決方案。 因為我繼續我的搜索,我也使用JSON_MERGE函數自己JSON_MERGE了一個解決方案。

當我執行更新時,我使用JSON_MERGE將一個空的 JSON 對象合並到數據庫中的字段上,對於所有帶有子節點的鍵,因此可以在數據庫的 JSON 字段中使用,然后,使用JSON_SET更新值。 所以完整的查詢如下所示:

UPDATE table SET
    -> attributes = JSON_MERGE(
    -> attributes, '{"test2": {}, "test4": {"test5": {}}}'),
    -> attributes = JSON_SET(attributes, "$.test2.test3", "Test 3");

執行此查詢后,結果將如下所示:

 mysql> SELECT * FROM testing;
 +----+---------------------------------------------------------------------------+
 | id | attributes                                                                |
 +----+---------------------------------------------------------------------------+
 |  1 | {"test1": "Test 1", "test2": {"test3": "Test 3"}, "test4": {"test5": {}}} |
 +----+---------------------------------------------------------------------------+
 1 row in set (0.00 sec)

我不知道目前哪種方法更好,目前都有效。 將來會進行一些速度測試,以檢查它們在 1 次更新 10.000 行時如何執行!

在像你們中的許多人一樣到處搜索后,我找到了這里列出的最佳解決方案: https : //forums.mysql.com/read.php?20,647956,647969#msg-647969

從站點:他節點和子節點,但不包含任何數據......所以在上面的例子中,對象將是這樣的:

{"nodes": {}} 

執行更新時,我使用JSON_MERGE將空的 JSON 對象合並到數據庫中的字段上,因此所有節點/子節點都可以在 te 數據庫的 JSON 字段中使用,然后使用JSON_SET更新值。 所以完整的查詢如下所示:

UPDATE table SET attributes = JSON_MERGE(attributes, '{"nodes": {}'), attributes = JSON_SET(attributes, "$.nodes.node2", "Node 2") 

目前,這是有效的。 但這是一個奇怪的解決方法。 也許這可以在以前的 MySQL 版本中查看,所以JSON_SET也會在設置子節點時創建父節點?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM