Сохранение данных с использованием параметров JSON
Эта статья показывает, как сохранять данные с динамическими колонками, используя хранимые процедуры с JSON-параметрами.
Поддерживаются пять встроенных параметров:
- @json_values_f1 (или @JsonValuesF1)
- @json_values_f2 (или @JsonValuesF2)
- @json_changes_f1 (или @JsonChangesF1)
- @json_changes_f2 (или @JsonChangesF2)
- @json_changes_f3 (или @JsonChangesF3)
@json_values_f1 и @json_values_f2 получают все значения строки, @json_values_f1 как массив и @json_values_f2 как объект.
@json_changes_f1 и @json_changes_f2 получают все изменения, @json_changes_f1 как массивы и @json_values_f2 как объекты.
@json_changes_f3 аналогичен @json_changes_f2, но всегда получает полный набор, включая колонки с пустыми значениями.
"f1", "f2" и "f3" - это просто индексы формата.
Также являются полезными следующие встроенные параметры:
- @json_columns (или @JsonColumns) получает актуальный набор имен колонок.
- @table_name (или @TableName) получает имя объекта запроса.
- @edit_action (или @EditAction) получает значения "INSERT", "UPDATE", "DELETE" или "MERGE" в зависимости от операции.
Настройка
Есть три подхода к привязке процедур сохранения изменений:
- Использование таблицы xls.objects
- Использование представления списка запросов
- Использование суффиксов
Ниже приведен пример настройки в таблице xls.objects:
ID | TABLE_SCHEMA | TABLE_NAME | TABLE_TYPE | TABLE_CODE | INSERT_OBJECT | UPDATE_OBJECT | DELETE_OBJECT |
---|---|---|---|---|---|---|---|
s02 | view_cashbook | VIEW | s02.view_cashbook_insert | s02.view_cashbook_update | s02.view_cashbook_delete | ||
s02 | usp_cashbook | PROCEDURE | s02.usp_cashbook_update |
Ниже приведен пример настройки для представления списка запросов:
ID | TABLE_SCHEMA | TABLE_NAME | TABLE_TYPE | TABLE_CODE | INSERT_PROCEDURE | UPDATE_PROCEDURE | DELETE_PROCEDURE | PROCEDURE_TYPE |
---|---|---|---|---|---|---|---|---|
s02 | view_cashbook | VIEW | s02.view_cashbook_insert | s02.view_cashbook_update | s02.view_cashbook_delete | |||
s02 | usp_cashbook | PROCEDURE | s02.usp_cashbook_update |
Все настройки выше не являются обязательными, т.к. продукты SaveToDB привязывают процедуры автоматически по суффиксам _insert, _update и _delete.
Соответственно, вы можете использовать эту технику даже без установленного SaveToDB Framework.
Вторые строки конфигурации используют единственную процедуру UPDATE. Используйте этот подход для процедур с параметрами json_changes, или когда для всех операций используется одна и та же процедура (см. ниже).
Детали реализации
SaveToDB и DBEdit выполняют заданные процедуры, передавая JSON в параметры.
DBGate и ODataDB выполняют процедуры на стороне сервера, формируя JSON для параметров на основе команд POST, PUT, DELETE.
Примеры
Ниже представлены детальные примеры для каждого случая.
- Пример для @json_values_f1
- Пример для @json_values_f2
- Использование универсальной процедуры обновления строк
- Пример для @json_changes_f1
- Пример для @json_changes_f2
- Использование универсальной процедуры обновления таблиц
Пример для @json_values_f1
Ниже представлен пример команд для процедур insert, update и delete:
EXEC [s24].[view_cashbook_json_values_f1_delete] @id = 21; EXEC [s24].[view_cashbook_json_values_f1_update] @json_columns = N'["id","date","account_id","item_id","company_id","debit","credit"]' , @json_values_f1 = N'[1,"2022-01-10",1,1,1,200000,null]'; EXEC [s24].[view_cashbook_json_values_f1_insert] @json_columns = N'["id","date","account_id","item_id","company_id","debit","credit"]' , @json_values_f1 = N'[21,"2022-03-31",1,2,8,null,100000]';
Ниже приведен пример хранимых процедур Microsoft SQL Server, которые обновляют таблицу, используя значения JSON.
Обратите внимание, что процедура delete использует обычный параметр @id.
CREATE PROCEDURE [s24].[view_cashbook_json_values_f1_insert] @json_columns nvarchar(max) , @json_values_f1 nvarchar(max) AS BEGIN SET @json_values_f1 = '[' + @json_values_f1 + ']' -- Fix for OPENJSON top-level array INSERT INTO s24.cashbook ([date], account_id, item_id, company_id, debit, credit) SELECT t2.[date], t2.account_id, t2.item_id, t2.company_id, t2.debit, t2.credit FROM OPENJSON(@json_values_f1) WITH ( [id] int '$[0]' , [date] date '$[1]' , [account_id] int '$[2]' , [item_id] int '$[3]' , [company_id] int '$[4]' , [debit] float '$[5]' , [credit] float '$[6]' ) t2; END GO CREATE PROCEDURE [s24].[view_cashbook_json_values_f1_update] @json_columns nvarchar(max) , @json_values_f1 nvarchar(max) AS BEGIN SET @json_values_f1 = '[' + @json_values_f1 + ']' -- Fix for OPENJSON top-level array UPDATE s24.cashbook SET [date] = t2.date , account_id = t2.account_id , item_id = t2.item_id , company_id = t2.company_id , debit = t2.debit , credit = t2.credit FROM s24.cashbook t INNER JOIN OPENJSON(@json_values_f1) WITH ( [id] int '$[0]' , [date] date '$[1]' , [account_id] int '$[2]' , [item_id] int '$[3]' , [company_id] int '$[4]' , [debit] float '$[5]' , [credit] float '$[6]' ) t2 ON t2.id = t.id END GO CREATE PROCEDURE [s24].[view_cashbook_json_values_f1_delete] @id int = NULL AS BEGIN DELETE FROM s24.json_test WHERE id = @id END GO
Пример для @json_values_f2
Ниже представлен пример команд для процедур insert, update и delete:
EXEC [s24].[view_cashbook_json_values_f2_delete] @id = 21; EXEC [s24].[view_cashbook_json_values_f2_update] @json_values_f2 = N'{"id":1,"date":"2022-01-10","account_id":1,"item_id":1,"company_id":1,"debit":200000,"credit":null}'; EXEC [s24].[view_cashbook_json_values_f2_insert] @json_values_f2 = N'{"id":21,"date":"2022-03-31","account_id":1,"item_id":2,"company_id":8,"debit":null,"credit":100000}';
В отличие от @json_values_f1, @json_values_f2 получает значения в виде объектов.
Ниже приведен пример хранимых процедур Microsoft SQL Server, которые обновляют таблицу, используя значения JSON.
CREATE PROCEDURE [s24].[view_cashbook_json_values_f2_insert] @json_values_f2 nvarchar(max) AS BEGIN INSERT INTO s24.cashbook ([date], account_id, item_id, company_id, debit, credit) SELECT t2.[date], t2.account_id, t2.item_id, t2.company_id, t2.debit, t2.credit FROM OPENJSON(@json_values_f2) WITH ( [id] int '$."id"' , [date] date '$."date"' , [account_id] int '$."account_id"' , [item_id] int '$."item_id"' , [company_id] int '$."company_id"' , [debit] float '$."debit"' , [credit] float '$."credit"' ) t2 END GO CREATE PROCEDURE [s24].[view_cashbook_json_values_f2_update] @json_values_f2 nvarchar(max) AS BEGIN UPDATE s24.cashbook SET [date] = t2.date , account_id = t2.account_id , item_id = t2.item_id , company_id = t2.company_id , debit = t2.debit , credit = t2.credit FROM s24.cashbook t INNER JOIN OPENJSON(@json_values_f2) WITH ( [id] int '$."id"' , [date] date '$."date"' , [account_id] int '$."account_id"' , [item_id] int '$."item_id"' , [company_id] int '$."company_id"' , [debit] float '$."debit"' , [credit] float '$."credit"' ) t2 ON t2.id = t.id END GO CREATE PROCEDURE [s24].[view_cashbook_json_values_f2_delete] @id int = NULL AS BEGIN DELETE FROM s24.cashbook WHERE id = @id END GO
Использование универсальной процедуры обновления строк
Примеры выше демонстрируют простой случай, когда структура обновляемой таблицы известна.
Т.к. параметр @json_values_f2 получает значения для всех колонок, можно создать полностью универсальную процедуру для сохранения изменений.
По ссылке приведен пример такой процедуры для Microsoft SQL Server:
https://dbgate.savetodb.com/api/mssql-241/en-us/s24.xl_update_generic_row/$definition
В дополнение к значениям строки, процедура имеет параметры целевой таблицы и действия.
Для вызова процедуры можно создать процедуру-обертку или SQL код и использовать их в поле UPDATE_OBJECT таблицы конфигурации.
CREATE PROCEDURE [s24].[view_cashbook_json_generic_row_update] @id int = NULL , @table_name nvarchar(255) = NULL , @edit_action nvarchar(6) = NULL , @json_values_f2 nvarchar(max) = NULL AS BEGIN EXEC s24.xl_update_generic_row @id, '[s24].[cashbook]', @edit_action, @json_values_f2 END
Т.к. используется единая процедура для операций вставки, обновления и удаления, следует указать процедуру только в поле UPDATE_OBJECT.
Ниже приведен пример генерируемых команд:
EXEC [s24].[view_cashbook_json_generic_row_update] @id = 21 , @table_name = N'[s24].[view_cashbook_json_generic_row]' , @edit_action = N'DELETE' , @json_values_f2 = N'{"id":21,"date":"2022-03-31","account_id":1,"item_id":2,"company_id":8,"debit":null,"credit":100000}'; EXEC [s24].[view_cashbook_json_generic_row_update] @id = 1 , @table_name = N'[s24].[view_cashbook_json_generic_row]' , @edit_action = N'UPDATE' , @json_values_f2 = N'{"id":1,"date":"2022-01-10","account_id":1,"item_id":1,"company_id":1,"debit":200000,"credit":null}'; EXEC [s24].[view_cashbook_json_generic_row_update] @id = 21 , @table_name = N'[s24].[view_cashbook_json_generic_row]' , @edit_action = N'INSERT' , @json_values_f2 = N'{"id":21,"date":"2022-03-31","account_id":1,"item_id":2,"company_id":8,"debit":null,"credit":100000}';
Обратите внимание, что все процедуры получают имя представления, которое используется для получения данных, а не целевой таблицы.
Именно поэтому требуется использование процедуры-обертки или SQL-кода, чтобы вызвать универсальную процедуру с правильным именем целевой таблицы.
Пример для @json_changes_f1
Ниже приведен пример команд сохранения изменений для процедуры с параметром @json_changes_f1:
EXEC [s24].[view_cashbook_json_changes_f1_update] @id = NULL , @json_changes_f1 = N'{ "table_name":"[s24].[view_cashbook_json_changes_f1]", "actions":{ "insert":{ "action":"insert" , "columns":["id","date","account_id","item_id","company_id","debit","credit"] , "rows":[[21,"2022-03-31",1,2,8,null,100000]] } ,"update":{ "action":"update" , "columns":["id","date","account_id","item_id","company_id","debit","credit"] , "rows":[[1,"2022-01-10",1,1,1,200000,null]] } ,"delete":{ "action":"delete" , "columns":["id"] , "rows":[[21]] } } }';
SaveToDB и DBEdit генерируют единственную команду для всех изменений и выполняют единственный вызов процедуры.
Это позволяет радикально увеличить общую производительность.
DBGate и ODataDB для таких объектов создают обычный API, получают обычные запросы POST, PUT и DELETE в едином пакетном запросе, и выполняют процедуру с параметрами JSON на стороне сервера.
Ниже приведен пример процедуры SQL Server, которая использует JSON-массивы значений для обновления таблицы.
CREATE PROCEDURE [s24].[view_cashbook_json_changes_f1_update] @id int = NULL , @json_changes_f1 nvarchar(max) = NULL AS BEGIN SET NOCOUNT ON DECLARE @insert nvarchar(max), @update nvarchar(max), @delete nvarchar(max) SELECT @insert = t2.[insert] , @update = t2.[update] , @delete = t2.[delete] FROM OPENJSON(@json_changes_f1) WITH ( actions nvarchar(max) AS json ) t1 CROSS APPLY OPENJSON(t1.actions) WITH ( [insert] nvarchar(max) '$.insert' AS json , [update] nvarchar(max) '$.update' AS json , [delete] nvarchar(max) '$.delete' AS json ) t2 IF @insert IS NOT NULL INSERT INTO s24.cashbook ([date], account_id, item_id, company_id, debit, credit) SELECT t2.[date], t2.account_id, t2.item_id, t2.company_id, t2.debit, t2.credit FROM OPENJSON(@insert) WITH ( [rows] nvarchar(max) '$.rows' AS json ) t1 CROSS APPLY OPENJSON(t1.[rows]) WITH ( [id] int '$[0]' , [date] date '$[1]' , [account_id] int '$[2]' , [item_id] int '$[3]' , [company_id] int '$[4]' , [debit] float '$[5]' , [credit] float '$[6]' ) t2; IF @update IS NOT NULL UPDATE s24.cashbook SET [date] = t2.[date] , account_id = t2.account_id , item_id = t2.item_id , company_id = t2.company_id , debit = t2.debit , credit = t2.credit FROM s24.cashbook t INNER JOIN ( SELECT t2.id AS id , t2.[date] AS [date] , t2.account_id AS account_id , t2.item_id AS item_id , t2.company_id AS company_id , t2.debit AS debit , t2.credit AS credit FROM OPENJSON(@update) WITH ( [rows] nvarchar(max) '$.rows' AS json ) t1 CROSS APPLY OPENJSON(t1.[rows]) WITH ( [id] int '$[0]' , [date] date '$[1]' , [account_id] int '$[2]' , [item_id] int '$[3]' , [company_id] int '$[4]' , [debit] float '$[5]' , [credit] float '$[6]' ) t2 ) t2 ON t2.id = t.id IF @delete IS NOT NULL DELETE FROM s24.cashbook FROM s24.cashbook t INNER JOIN ( SELECT t2.[id] AS [id] FROM OPENJSON(@delete) WITH ( [rows] nvarchar(max) '$.rows' AS json ) t1 CROSS APPLY OPENJSON(t1.[rows]) WITH ( [id] int '$[0]' ) t2 ) t2 ON t2.[id] = t.[id] END
Пример для @json_changes_f2
Ниже приведен пример команд для процедуры с параметром @json_changes_f2:
EXEC [s24].[view_cashbook_json_changes_f2_update] @id = NULL , @json_changes_f2 = N'{ "table_name":"[s24].[view_cashbook_json_changes_f2]" , "actions":{ "insert":{ "action":"insert" , "columns":["id","date","account_id","item_id","company_id","debit","credit"] , "rows":[{"id":21,"date":"2022-03-31","account_id":1,"item_id":2,"company_id":8,"debit":null,"credit":100000}] } ,"update":{ "action":"update" , "columns":["id","date","account_id","item_id","company_id","debit","credit"], , "rows":[{"id":1,"date":"2022-01-10","account_id":1,"item_id":1,"company_id":1,"debit":200000,"credit":null}] } , "delete":{ "action":"delete" , "columns":["id"] , "rows":[{"id":21}] } } }';
В отличие от @json_changes_f1, @json_changes_f3 получает значения в виде объектов.
Ниже приведен пример хранимой процедуры SQL Server, которая использует значения объектов JSON для обновления таблицы.
CREATE PROCEDURE [s24].[view_cashbook_json_changes_f2_update] @id int = NULL , @json_changes_f2 nvarchar(max) = NULL AS BEGIN SET NOCOUNT ON DECLARE @insert nvarchar(max), @update nvarchar(max), @delete nvarchar(max) SELECT @insert = t2.[insert] , @update = t2.[update] , @delete = t2.[delete] FROM OPENJSON(@json_changes_f2) WITH ( actions nvarchar(max) AS json ) t1 CROSS APPLY OPENJSON(t1.actions) WITH ( [insert] nvarchar(max) '$.insert' AS json , [update] nvarchar(max) '$.update' AS json , [delete] nvarchar(max) '$.delete' AS json ) t2 IF @insert IS NOT NULL INSERT INTO s24.cashbook ([date], account_id, item_id, company_id, debit, credit) SELECT t2.[date], t2.account_id, t2.item_id, t2.company_id, t2.debit, t2.credit FROM OPENJSON(@insert) WITH ( [rows] nvarchar(max) '$.rows' AS json ) t1 CROSS APPLY OPENJSON(t1.[rows]) WITH ( [id] int '$."id"' , [date] date '$."date"' , [account_id] int '$."account_id"' , [item_id] int '$."item_id"' , [company_id] int '$."company_id"' , [debit] float '$."debit"' , [credit] float '$."credit"' ) t2; IF @update IS NOT NULL UPDATE s24.cashbook SET [date] = t2.[date] , account_id = t2.account_id , item_id = t2.item_id , company_id = t2.company_id , debit = t2.debit , credit = t2.credit FROM s24.cashbook t INNER JOIN ( SELECT t2.id AS id , t2.[date] AS [date] , t2.account_id AS account_id , t2.item_id AS item_id , t2.company_id AS company_id , t2.debit AS debit , t2.credit AS credit FROM OPENJSON(@update) WITH ( [rows] nvarchar(max) '$.rows' AS json ) t1 CROSS APPLY OPENJSON(t1.[rows]) WITH ( [id] int '$."id"' , [date] date '$."date"' , [account_id] int '$."account_id"' , [item_id] int '$."item_id"' , [company_id] int '$."company_id"' , [debit] float '$."debit"' , [credit] float '$."credit"' ) t2 ) t2 ON t2.id = t.id; IF @delete IS NOT NULL DELETE FROM s24.cashbook FROM s24.cashbook t INNER JOIN ( SELECT t2.[id] AS [id] FROM OPENJSON(@delete) WITH ( [rows] nvarchar(max) '$.rows' AS json ) t1 CROSS APPLY OPENJSON(t1.[rows]) WITH ( [id] int '$."id"' ) t2 ) t2 ON t2.id = t.id END
Использование универсальной процедуры обновления таблиц
Примеры выше можно использовать, если структура таблицы данных известна.
Можно создать полностью универсальную процедуру для сохранения изменений в таблицу.
По ссылке приведен пример такой процедуры для Microsoft SQL Server:
https://dbgate.savetodb.com/api/mssql-241/en-us/s24.xl_update_generic_table/$definition
В дополнение к данным изменений, процедура получает имя целевой таблицы.
Для процедуры сохранения изменений необходимо определить параметры для полей первичного ключа, которые передаются для операций удаления.
Для вызова таких процедур можно создать процедуру-обертку, представленную ниже, или же использовать SQL-код в поле UPDATE_OBJECT таблицы конфигурации.
CREATE PROCEDURE [s24].[view_cashbook_json_generic_table_update] @id int = NULL , @table_name nvarchar(255) = NULL , @json_changes_f2 nvarchar(max) = NULL AS BEGIN EXEC s24.xl_update_generic_table '[s24].[cashbook]', @json_changes_f2 END