commit e7f672ddff58528d0623bc766c1e916038e754f9 Author: DATAEXPRESS\4734 Date: Fri Feb 6 14:31:07 2026 +0800 first commit diff --git a/execute_multiple_sql.txt b/execute_multiple_sql.txt new file mode 100644 index 0000000..cd15aff --- /dev/null +++ b/execute_multiple_sql.txt @@ -0,0 +1,29 @@ +DELIMITER // +CREATE OR REPLACE PROCEDURE execute_multiple_sql(IN v_sql TEXT) +BEGIN + DECLARE EXIT HANDLER FOR SQLEXCEPTION + BEGIN + GET DIAGNOSTICS CONDITION 1 + @sqlstate = RETURNED_SQLSTATE, + @errno = MYSQL_ERRNO, + @text = MESSAGE_TEXT; + + SELECT 1 AS code, CONCAT('Error: ', @errno, ' (', @sqlstate, '): ', @text) AS msg; + END; + + -- 按分号分割并执行多条语句 + SET @sql = v_sql; + WHILE LENGTH(@sql) > 0 DO + SET @statement = SUBSTRING_INDEX(@sql, ';', 1); + SET @sql = SUBSTRING(@sql, LENGTH(@statement) + 2); + + IF LENGTH(TRIM(@statement)) > 0 THEN + PREPARE stmt FROM @statement; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; + END WHILE; + + SELECT 0 AS code, 'ok' AS msg; +END// +DELIMITER ; \ No newline at end of file diff --git a/gf_split.txt b/gf_split.txt new file mode 100644 index 0000000..85fcf0e --- /dev/null +++ b/gf_split.txt @@ -0,0 +1,67 @@ +DELIMITER // + +CREATE OR REPLACE FUNCTION gf_split( + p_source TEXT, + p_split VARCHAR(2) +) RETURNS TEXT +/* + function : 將傳入的字串, 依 p_split 切割為多筆資料, 傳回 table + description: + 呼叫範例: + SELECT col FROM JSON_TABLE(gf_split('R,G,B',','), '$[*]' + COLUMNS(col VARCHAR(600) PATH '$.col')) t; + + SELECT col FROM JSON_TABLE(gf_split('R,G,B',','), '$[*]' + COLUMNS(rowid int PATH '$.rowid', col VARCHAR(600) PATH '$.col')) t WHERE rowid = 1; + + 必須要有 CREATE TABLE _split( guid varchar(100), rowid INT, col varchar(600) ); + + https://www.sqlines.com/sql-server-to-mariadb/returns_table + build date : 2023/11/20 + + modify history + item who date modify docs + ==== ========== ========== ================================================ + 1 michael 2011/09/08 新增 + 2 Michael 2025/07/11 改寫為MariaDB版本並優化 +*/ +BEGIN + DECLARE v_pos INT; + DECLARE v_cnt INT; + DECLARE v_length INT; + DECLARE v_temp TEXT; + DECLARE p_uuid VARCHAR(100); + DECLARE data TEXT; + + SET v_length = LENGTH(p_split); + SET v_temp = p_source; + SET p_uuid = UUID(); + SET v_cnt = 1; + + -- 循環處理分割 + WHILE LOCATE(p_split, v_temp) > 0 DO + SET v_pos = LOCATE(p_split, v_temp); + INSERT INTO _split (guid, rowid, col) VALUES (p_uuid, v_cnt, SUBSTRING(v_temp, 1, v_pos - 1)); + + SET v_temp = SUBSTRING(v_temp, v_pos + v_length); + SET v_cnt = v_cnt + 1; + END WHILE; + + -- 添加最後一部分 + IF LENGTH(v_temp) > 0 THEN + INSERT INTO _split(guid, rowid, col) VALUES (p_uuid, v_cnt, v_temp); + END IF; + + SELECT JSON_ARRAYAGG(JSON_OBJECT('col', col)) INTO data + FROM + ( + SELECT col FROM _split WHERE guid = p_uuid + ) data; + + -- del temp data + delete from _split WHERE guid = p_uuid; + + RETURN data; +END // + +DELIMITER ; \ No newline at end of file diff --git a/gp_split.txt b/gp_split.txt new file mode 100644 index 0000000..8c38a7b --- /dev/null +++ b/gp_split.txt @@ -0,0 +1,54 @@ +DELIMITER // + +CREATE OR REPLACE PROCEDURE gp_split( + p_uuid VARCHAR(100), + p_source TEXT, + p_split VARCHAR(2) +) +/* + function : 將傳入的字串, 依 p_split 切割為多筆資料, 傳回暫存 table + description: + 呼叫範例: + SET p_uuid = UUID(); + CALL gp_split(p_uuid, 'R,G,B',','); + select col from _split where guid = p_uuid and rowid = 1; + DELETE FROM _split WHERE guid = p_uuid; + + 必須要有 CREATE TABLE _split( guid varchar(100), rowid INT, col varchar(600) ); + + https://www.sqlines.com/sql-server-to-mariadb/returns_table + build date : 2023/11/20 + + modify history + item who date modify docs + ==== ========== ========== ================================================ + 1 michael 2011/09/08 新增 + 2 Michael 2025/07/11 改寫為MariaDB版本並優化 +*/ +BEGIN + DECLARE v_pos INT; + DECLARE v_cnt INT; + DECLARE v_length INT; + DECLARE v_temp TEXT; + + SET v_length = LENGTH(p_split); + SET v_temp = p_source; + SET v_cnt = 1; + + -- 循環處理分割 + WHILE LOCATE(p_split, v_temp) > 0 DO + SET v_pos = LOCATE(p_split, v_temp); + INSERT INTO _split (guid, rowid, col) VALUES (p_uuid, v_cnt, SUBSTRING(v_temp, 1, v_pos - 1)); + + SET v_temp = SUBSTRING(v_temp, v_pos + v_length); + SET v_cnt = v_cnt + 1; + END WHILE; + + -- 添加最後一部分 + IF LENGTH(v_temp) > 0 THEN + INSERT INTO _split(guid, rowid, col) VALUES (p_uuid, v_cnt, v_temp); + END IF; + +END // + +DELIMITER ; \ No newline at end of file diff --git a/orm_api_kernel_v2.txt b/orm_api_kernel_v2.txt new file mode 100644 index 0000000..cb0672d --- /dev/null +++ b/orm_api_kernel_v2.txt @@ -0,0 +1,345 @@ +DELIMITER // + +CREATE OR REPLACE PROCEDURE orm_api_kernel_v2( + IN p_tablename VARCHAR(100), + IN p_pk VARCHAR(100), -- primary key(for update delete) + IN p_action VARCHAR(20), -- CRUD + IN p_uuid VARCHAR(100), + IN p_json_id INT +) +/* + Function : 通用 orm-api 根据传入参数及设定,智能生成相应SQL + Description: 模仿 ORM 设定,让前端程序只需传入参数就能执行增删查改基本功能 + Build Date : 2024-03-20 + + call orm_api_kernel_v2('pms_sprint','sprintid','U','uuid-20250803_2',0); + + Modify History + Item Who Date Modify Docs + ==== ========== ========== ================================================ + 1 Developer 2024/03/20 从 MS-SQL v2 版本转换为 MariaDB 版本并优化 +*/ +BEGIN + DECLARE v_field_identity VARCHAR(100); + DECLARE v_fields TEXT; + DECLARE v_datas TEXT; + DECLARE v_updatedatas TEXT; + DECLARE v_pkwhere TEXT; + DECLARE v_sql LONGTEXT; + DECLARE v_pk_cnt INT; + DECLARE v_pk_no INT; + DECLARE v_max_no VARCHAR(20); + DECLARE v_bill_uuid VARCHAR(100); + DECLARE v_bill_head VARCHAR(20); + DECLARE v_middlefield VARCHAR(100); + DECLARE v_dest_table VARCHAR(100); + DECLARE v_pk_id VARCHAR(50); + DECLARE v_fieldname VARCHAR(100); + DECLARE v_contents LONGTEXT; + DECLARE v_fieldtype VARCHAR(20); + DECLARE v_quot CHAR(1); + DECLARE v_cnt INT; + DECLARE v_cnt2 INT; + DECLARE v_maxcount INT; + DECLARE v_currid INT; + DECLARE v_nextrowid INT; + DECLARE v_crlf VARCHAR(2); + + -- 创建临时表存储JSON数据 + CREATE TEMPORARY TABLE IF NOT EXISTS temp_jsondata ( + parent_id INT, + json_key VARCHAR(100), + json_value LONGTEXT, + ValueType VARCHAR(20), + rowid INT AUTO_INCREMENT PRIMARY KEY + ); + + -- 临时表用于同步删除 + CREATE TEMPORARY TABLE IF NOT EXISTS temp_sync_del ( + dest_table VARCHAR(100), + pk_id VARCHAR(50), + rowid INT AUTO_INCREMENT PRIMARY KEY + ); + + -- 临时表用于避免重复字段名 + CREATE TEMPORARY TABLE IF NOT EXISTS temp_comp_json ( + fieldname VARCHAR(100) + ); + + -- 清空临时表 + TRUNCATE TABLE temp_jsondata; + TRUNCATE TABLE temp_sync_del; + TRUNCATE TABLE temp_comp_json; + + -- 将传入参数转为JSON数据(多筆 Grid 資料寫入) + IF p_json_id = 1 then + INSERT INTO temp_jsondata (parent_id, json_key, json_value, ValueType) + SELECT parent_id, json_key, json_value, ValueType + FROM orm_json_tmp + WHERE uuid = p_uuid; + else + INSERT INTO temp_jsondata (parent_id, json_key, json_value, ValueType) + SELECT parent_id, json_key, json_value, ValueType + FROM orm_json_tmp + WHERE uuid = p_uuid AND parent_id = FLOOR(p_json_id / 1000) and object_id = p_json_id - FLOOR(p_json_id / 1000)*1000; + end if; + + -- 获取自增字段 + SELECT COLUMN_NAME INTO v_field_identity + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = p_tablename + AND EXTRA = 'auto_increment' + LIMIT 1; + + -- 初始化变量 + SET v_fields = ''; + SET v_datas = ''; + SET v_pkwhere = ''; + SET v_updatedatas = ''; + SET v_pk_cnt = 0; + SET v_bill_uuid = ''; + SET v_bill_head = ''; + SET v_crlf = CONCAT(CHAR(13), CHAR(10)); + + -- 获取记录数 + SELECT COUNT(*) INTO v_maxcount FROM temp_jsondata; + SELECT MIN(rowid) INTO v_currid FROM temp_jsondata; + + -- 遍历JSON数据构建SQL + WHILE v_currid <= v_maxcount DO + -- 获取当前记录 + SELECT json_key, IFNULL(json_value, ''), ValueType + INTO v_fieldname, v_contents, v_fieldtype + FROM temp_jsondata + WHERE rowid = v_currid; + + -- 设置引号 + IF lower(v_fieldtype) = 'string' THEN + SET v_quot = ''''; + ELSE + SET v_quot = ''; + END IF; + + -- 记录billno和uuid用于MD更新 + IF v_fieldname = 'uuid' THEN + SET v_bill_uuid = v_contents; + END IF; + IF v_fieldname = 'billno' THEN + SET v_bill_head = v_contents; + END IF; + + -- 检查字段是否存在 + SELECT COUNT(*) INTO v_cnt + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = p_tablename AND COLUMN_NAME = v_fieldname; + + SELECT COUNT(*) INTO v_cnt2 + FROM temp_comp_json + WHERE fieldname = v_fieldname; + + -- 如果字段存在且未处理过 + IF v_cnt > 0 AND v_cnt2 <= 0 THEN + -- 记录主键内容 + SELECT COUNT(*) INTO v_pk_no + FROM ( + SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(p_pk, ',', numbers.n), ',', -1) AS col + FROM ( + SELECT 1 AS n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL + SELECT 4 UNION ALL SELECT 5 -- 假设最多5个主键字段 + ) numbers + WHERE numbers.n <= LENGTH(p_pk) - LENGTH(REPLACE(p_pk, ',', '')) + 1 + ) AS split_pk + WHERE col = v_fieldname; + + IF v_pk_no > 0 THEN + IF v_pk_cnt >= 1 THEN + UPDATE sys_transaction SET para02 = v_contents WHERE tablename = p_tablename; + ELSE + UPDATE sys_transaction SET para01 = v_contents WHERE tablename = p_tablename; + END IF; + SET v_pk_cnt = v_pk_cnt + 1; + END IF; + + -- 非自增字段处理 + IF IFNULL(v_field_identity, '') <> v_fieldname THEN + SET v_fields = CONCAT(v_fields, v_fieldname, ','); + + -- 构建INSERT值 + IF p_action = 'C' OR p_action = 'Insert' THEN + SET v_datas = CONCAT(v_datas, v_quot, v_contents, v_quot, ','); + END IF; + END IF; + + -- 构建UPDATE语句 + IF (p_action = 'U' OR p_action = 'Update') AND v_pk_no = 0 THEN + -- 处理日期格式 + IF SUBSTRING(v_contents, 11, 19) = 'T08:00:00.000+08:00' THEN + SET v_contents = SUBSTRING(v_contents, 1, 10); + END IF; + + SET v_updatedatas = CONCAT(v_updatedatas, v_fieldname, ' = ', v_quot, v_contents, v_quot, ','); + END IF; + + -- 构建WHERE条件 + IF v_pk_no > 0 THEN + SET v_pkwhere = CONCAT(v_pkwhere, v_fieldname, ' = ', v_quot, v_contents, v_quot, ' AND '); + END IF; + END IF; + + -- 记录已处理字段 + INSERT INTO temp_comp_json VALUES (v_fieldname); + + -- 获取下一行 + SELECT MIN(rowid) INTO v_nextrowid + FROM temp_jsondata + WHERE rowid > v_currid; + SET v_currid = v_nextrowid; + END WHILE; + + -- 处理没有主键的情况 + IF v_pk_cnt = 0 THEN + SELECT middlefield INTO v_middlefield + FROM sys_transaction + WHERE tablename = p_tablename + LIMIT 1; + + SELECT json_value INTO v_contents + FROM temp_jsondata + WHERE json_key = v_middlefield + LIMIT 1; + + IF LENGTH(v_contents) <= 1000 THEN + UPDATE sys_transaction SET para01 = v_contents WHERE tablename = p_tablename; + END IF; + END IF; + + -- 处理自增主键情况 + IF (p_action = 'C' OR p_action = 'Insert') AND (p_pk = v_field_identity) THEN + UPDATE sys_transaction SET para01 = 'identity' WHERE tablename = p_tablename; + END IF; + + -- 移除末尾的逗号或AND + IF v_fields <> '' THEN + -- SET v_fields = SUBSTRING(v_fields, 1, LENGTH(v_fields) - 1); + SET v_fields = TRIM(TRAILING ',' FROM v_fields); + END IF; + + IF v_datas <> '' THEN + -- SET v_datas = SUBSTRING(v_datas, 1, LENGTH(v_datas) - 1); + SET v_datas = TRIM(TRAILING ',' FROM v_datas); + END IF; + + IF v_updatedatas <> '' THEN + -- SET v_updatedatas = SUBSTRING(v_updatedatas, 1, LENGTH(v_updatedatas) - 1); + SET v_updatedatas = TRIM(TRAILING ',' FROM v_updatedatas); + END IF; + + IF v_pkwhere <> '' THEN + SET v_pkwhere = SUBSTRING(v_pkwhere, 1, LENGTH(v_pkwhere) - 5); + END IF; + + -- 构建基础SQL + IF p_action = 'C' OR p_action = 'Insert' THEN + SET v_sql = CONCAT('INSERT INTO ', p_tablename, ' (', v_fields, ') VALUES (', v_datas, ')'); + + -- 加入 M-D 的 D 同步 billno update + INSERT INTO temp_sync_del (dest_table, pk_id) + SELECT dest_table, pk_id FROM sys_sync_delete_detail WHERE tablename = p_tablename; + + -- 处理同步更新 + SELECT COUNT(*) INTO v_maxcount FROM temp_sync_del; + SELECT MIN(rowid) INTO v_currid FROM temp_sync_del; + + WHILE v_currid <= v_maxcount DO + SELECT dest_table, pk_id INTO v_dest_table, v_pk_id + FROM temp_sync_del + WHERE rowid = v_currid; + + -- 检查目标表是否有uuid字段 + SELECT COUNT(*) INTO v_cnt + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = v_dest_table AND COLUMN_NAME = 'uuid'; + + IF v_cnt > 0 THEN + SET v_sql = CONCAT(v_sql, ';\n', + 'UPDATE ', v_dest_table, ' SET ', IFNULL(v_pk_id, 'billno'), ' = ''', + v_bill_head, ''' WHERE uuid = ''', v_bill_uuid, ''''); + END IF; + + -- 获取下一行 + SELECT MIN(rowid) INTO v_nextrowid + FROM temp_sync_del + WHERE rowid > v_currid; + SET v_currid = v_nextrowid; + END WHILE; + + ELSEIF p_action = 'U' OR p_action = 'Update' THEN + SET v_sql = CONCAT('UPDATE ', p_tablename, ' SET ', v_updatedatas, ' WHERE ', v_pkwhere); + + ELSEIF p_action = 'D' OR p_action = 'Delete' THEN + -- 基础删除语句 + SET v_sql = CONCAT('DELETE FROM ', p_tablename, ' WHERE ', v_pkwhere); + + -- 处理同步删除 + INSERT INTO temp_sync_del (dest_table, pk_id) + SELECT dest_table, pk_id FROM sys_sync_delete_detail WHERE tablename = p_tablename; + + SELECT COUNT(*) INTO v_maxcount FROM temp_sync_del; + SELECT MIN(rowid) INTO v_currid FROM temp_sync_del; + + WHILE v_currid <= v_maxcount DO + SELECT dest_table, pk_id INTO v_dest_table, v_pk_id + FROM temp_sync_del + WHERE rowid = v_currid; + + -- 解决 M-D 的 pk 栏名不同的问题 + IF IFNULL(v_pk_id, '') = '' THEN + SET v_sql = CONCAT(v_sql, ';', v_crlf, + 'DELETE FROM ', v_dest_table, ' WHERE ', v_pkwhere, ';'); + ELSE + SET v_sql = CONCAT(v_sql, ';', v_crlf, + 'DELETE FROM ', v_dest_table, ' WHERE ', + REPLACE(v_pkwhere, p_pk, v_pk_id), ';'); + END IF; + + -- 获取下一行 + SELECT MIN(rowid) INTO v_nextrowid + FROM temp_sync_del + WHERE rowid > v_currid; + SET v_currid = v_nextrowid; + END WHILE; + END IF; + + -- 处理日期格式问题 + IF INSTR(v_sql, '000+08:00') > 0 THEN + SET v_sql = REPLACE(v_sql, '000+08:00', '000Z'); + END IF; + + -- 执行SQL + /* Mariadb 不支援動態語句多 sql, 只能切割 + GET DIAGNOSTICS CONDITION 1 + @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT; + + SELECT 1 AS code, CONCAT('Error: ', @errno, ' (', @sqlstate, '): ', @text) AS msg, 0 AS recno; + SELECT v_sql AS error_sql; + END; + + -- 返回成功结果 + SELECT 0 AS code, 'ok' AS msg, 0 AS recno; + + -- 执行SQL + SET @dynamic_sql = v_sql; + PREPARE stmt FROM @dynamic_sql; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END; + */ + call execute_multiple_sql(v_sql); + + -- 清理临时表 + DROP TEMPORARY TABLE IF EXISTS temp_jsondata; + DROP TEMPORARY TABLE IF EXISTS temp_sync_del; + DROP TEMPORARY TABLE IF EXISTS temp_comp_json; +END // + +DELIMITER ; \ No newline at end of file diff --git a/orm_api_v2.txt b/orm_api_v2.txt new file mode 100644 index 0000000..a2becc5 --- /dev/null +++ b/orm_api_v2.txt @@ -0,0 +1,117 @@ +DELIMITER // + +CREATE OR REPLACE PROCEDURE orm_api_v2( + IN p_token VARCHAR(100), -- token(直接傳入) + IN p_tablename VARCHAR(200), -- tablename + IN p_pk VARCHAR(100), -- primary key(for update delete) + IN p_action VARCHAR(20), -- CRUDP(如果是 Read, p_paras 參數內容有特殊寫法) + IN p_paras LONGTEXT -- 前端組好的資料內容(應該是 json 格式的內容) +) +/* + Function : 寫一支通用 orm-api 依傳入的參數及設定, 智慧的組出相應的 sql + MariaDB 版本 + + Description: 本程式是想模仿 orm 的設定, 然後讓前端程式只要傳入參數, 就能執行 orm 的增刪查改的基本功能 + R(Read) 是讀取單筆資料, p_paras 的參數內容同 CU >> 取消不用 + P(Page) 是以頁為單位的讀取模式, 一般是 cowork with DataGrid + + 呼叫 api 範例如下 + --新增資料 + CALL orm_api_v2('B9F290D4-0F57-45B1-A786-F83A63E8BC4D','basarea','areaid','C','{"areaid":"mm", "areacnmae": "Michael"}'); + CALL orm_api_v2('token_value','basarea','areaid','U','') --修改資料 + CALL orm_api_v2('token_value','basarea','areaid','D','') --刪除資料 + + (pass)CALL orm_api_v2('B9F290D4-0F57-45B1-A786-F83A63E8BC4D','basarea','areaid','P','2^5^areaid^*'); -- 分頁查詢 + -- 新增 Query 後要分頁顯示(擴充 P 的功能, 多傳 wheresql) + SET @para = CONCAT('areaid LIKE ',QUOTE('%A%')); + CALL orm_api_v2('token_value','basarea','areaid','P','1^10^fieldid^*^functiontag,tablename,fieldname^zen01'); + +-- 多筆資料 update +call orm_api_v2('aa54ef9b-7139-11f0-9c5f-d8bbc15c1404','bascustcategory','custcateid','U', +'{"rows":[ +{"custcateid":"03","custcatecname":"大型企業","custcateename":"555","memochar":"","mergetype":"0"}, +{"custcateid":"04","custcatecname":"政府","custcateename":"666","memochar":"","mergetype":"0"}], +"rowsDiff":[ +{"custcateename":"555"},{"custcateename":"666"}],"indexes":["2","3"], +"rowsOrigin":[{"custcateid":"03","custcatecname":"大型企業","custcateename":"","memochar":"","mergetype":"0"}, +{"custcateid":"04","custcatecname":"政府","custcateename":"","memochar":"","mergetype":"0"}], +"unModifiedItems":[ +{"custcateid":"01","custcatecname":"經銷商","custcateename":"333","memochar":"","mergetype":"0"}, +{"custcateid":"02","custcatecname":"代理商","custcateename":"444","memochar":"","mergetype":"0"}, +{"custcateid":"05","custcatecname":"中小企業","custcateename":"","memochar":"","mergetype":"0"}, +{"custcateid":"06","custcatecname":"學校","custcateename":"","memochar":"","mergetype":"0"}, +{"custcateid":"07","custcatecname":"竹科","custcateename":"","memochar":"","mergetype":"0"}] +,"token":"aa54ef9b-7139-11f0-9c5f-d8bbc15c1404", +"_$_tableName":"bascustcategory", +"_$_pk":"custcateid","_$_action":"U"}'); + +SELECT * FROM orm_json_tmp; +delete FROM orm_json_tmp; + + + Build Date : 2023/12/16 (原始版本) + Modify Date: 2023/12/16 (MariaDB 遷移版本) +*/ +BEGIN + DECLARE v_userid VARCHAR(100); + DECLARE v_uuid VARCHAR(36); + DECLARE EXIT HANDLER FOR SQLEXCEPTION + BEGIN + ROLLBACK; + RESIGNAL; + END; + + -- 驗證 token + SELECT userid INTO v_userid FROM sys_users WHERE token = p_token LIMIT 1; + + IF v_userid IS NULL THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'token 錯誤, 請洽系統管理員!!'; + END IF; + + -- 整理 json 資料 + SET p_paras = orm_retrive_json_v2(p_paras); + + -- 刪除過期的暫存資料 + DELETE FROM orm_json_tmp WHERE create_date < DATE_SUB(NOW(), INTERVAL 1 DAY); + + -- 根據操作類型執行不同邏輯 + IF p_action IN ('C', 'Insert', 'U', 'Update', 'D', 'Delete') THEN + -- 開啟事務 + START TRANSACTION; + + -- 處理 JSON 資料 + SET p_paras = REPLACE(p_paras, '": "', '":"'); + SET v_uuid = UUID(); + + -- call usp_parsejson(v_uuid,p_paras); + CALL parse_JSON(v_uuid, p_paras); + + -- 將傳入的 json 寫入暫存表方便處理 + INSERT INTO orm_json_tmp (uuid, parent_id, object_id, json_key, json_value, valuetype, create_date) + SELECT v_uuid, parent_id, object_id, name, stringvalue, ValueType, NOW() + FROM sys_jsondata WHERE UUID = v_uuid and valuetype <> 'OBJECT' and valuetype <> 'ARRAY'; + -- 測試時先 Mark + DELETE FROM sys_jsondata WHERE UUID = v_uuid; + + -- 根據操作類型調用不同副程式 + IF p_action IN ('C', 'Insert') THEN + CALL orm_exec_insert_v2(p_tablename, p_pk, p_action, v_uuid, v_userid); + ELSEIF p_action IN ('U', 'Update') THEN + CALL orm_exec_update_v2(p_tablename, p_pk, p_action, v_uuid, v_userid); + ELSEIF p_action IN ('D', 'Delete') THEN + CALL orm_api_kernel_v2(p_tablename, p_pk, p_action, v_uuid, 1); + END IF; + + -- 存檔後過帳機制 + CALL orm_exec_aftersave_v2(p_tablename, p_pk, p_action, v_userid); + + -- 提交事務 + COMMIT; + + ELSEIF p_action IN ('P', 'Page') THEN + -- 核心的 orm 程式, 取得顯示在 DataTable 的分頁資料 + CALL orm_exec_page_v2(p_tablename, p_pk, v_userid, p_paras); + END IF; +END // + +DELIMITER ; diff --git a/orm_exec_aftersave_v2.txt b/orm_exec_aftersave_v2.txt new file mode 100644 index 0000000..b52050d --- /dev/null +++ b/orm_exec_aftersave_v2.txt @@ -0,0 +1,100 @@ +DELIMITER // + +CREATE OR REPLACE PROCEDURE orm_exec_aftersave_v2( + IN p_tablename VARCHAR(100), + IN p_pk VARCHAR(100), -- primary key(for update delete) + IN p_action VARCHAR(10), + IN p_userid VARCHAR(100) +) +/* + 功能: orm_api_v2 [存档后处理]的核心程序,智能构建相应SQL + 描述: 用于保存后执行额外处理逻辑 + 创建日期: 2023/12/16 + MariaDB转换版本 +*/ +BEGIN + DECLARE v_sp_name TEXT; + DECLARE v_para01 TEXT; + DECLARE v_para02 TEXT; + DECLARE v_sql_command TEXT; + DECLARE v_dest_table TEXT; + DECLARE v_dest_field TEXT; + DECLARE v_dest_field2 TEXT; + DECLARE v_RelationKey TEXT; + DECLARE v_filter_user_fieldname TEXT; + DECLARE v_max_id INT; + + -- 取得 sys_transaction 信息 + SELECT + sp_name, + para01, + para02, + dest_table, + dest_field, + dest_field2, + RelationKey, + filter_user_fieldname + INTO + v_sp_name, + v_para01, + v_para02, + v_dest_table, + v_dest_field, + v_dest_field2, + v_RelationKey, + v_filter_user_fieldname + FROM sys_transaction + WHERE tablename = p_tablename AND (actiontype = '*' OR LOCATE(p_action, actiontype) > 0) + LIMIT 1; + + IF v_sp_name IS NOT NULL AND v_sp_name != '' THEN + -- 检查是否为特殊情况 + IF (v_sp_name != 'sys_transfer_owned2rolemenusecurity') AND (v_sp_name != 'sys_transfer_owned2userroles') THEN + -- 如果 pk 是 identity + IF v_para01 = 'identity' THEN + -- 处理 identity 的情况 + IF IFNULL(v_filter_user_fieldname, '') = '' THEN + SET @sql = CONCAT('SELECT MAX(', p_pk, ') INTO @max_id FROM ', p_tablename); + PREPARE stmt FROM @sql; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + + SET v_sql_command = CONCAT('UPDATE sys_transaction SET para01 = ? WHERE tablename = ''', p_tablename, ''''); + PREPARE stmt FROM v_sql_command; + EXECUTE stmt USING @max_id; + DEALLOCATE PREPARE stmt; + ELSE + SET @sql = CONCAT('SELECT MAX(', p_pk, ') INTO @max_id FROM ', p_tablename, + ' WHERE ', v_filter_user_fieldname, ' = ''', p_userid, ''''); + PREPARE stmt FROM @sql; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + + SET v_sql_command = CONCAT('UPDATE sys_transaction SET para01 = ? WHERE tablename = ''', p_tablename, ''''); + PREPARE stmt FROM v_sql_command; + EXECUTE stmt USING @max_id; + DEALLOCATE PREPARE stmt; + END IF; + END IF; + + SET v_sql_command = CONCAT('CALL ', v_sp_name, '( ''', p_tablename, ''' )'); + ELSE + -- 针对特殊情况特别改写 sp 的方案 + IF IFNULL(v_para01, '') != '' AND IFNULL(v_para02, '') != '' THEN + SET v_sql_command = CONCAT('CALL ', v_sp_name, '( ''', v_para01, ''', ''', v_para02, ''' )'); + ELSEIF IFNULL(v_para01, '') != '' AND IFNULL(v_para02, '') = '' THEN + SET v_sql_command = CONCAT('CALL ', v_sp_name, '( ''', v_para01, ''' )'); + ELSE + SET v_sql_command = CONCAT('CALL ', v_sp_name, '()'); + END IF; + END IF; + + -- 执行 SQL + SET @dynamic_sql = v_sql_command; + PREPARE stmt FROM @dynamic_sql; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; +END // + +DELIMITER ; \ No newline at end of file diff --git a/orm_exec_insert_v2.txt b/orm_exec_insert_v2.txt new file mode 100644 index 0000000..c850aee --- /dev/null +++ b/orm_exec_insert_v2.txt @@ -0,0 +1,179 @@ +DELIMITER // + +CREATE OR REPLACE PROCEDURE orm_exec_insert_v2( + IN p_tablename VARCHAR(200), -- tablename + IN p_pk VARCHAR(100), -- primary key(for update delete) + IN p_action VARCHAR(20), -- CRUDP + IN p_uuid VARCHAR(100), + IN p_userid VARCHAR(100) +) +/* + Function : orm_api_v2 [新增]的核心程式, 智慧的組出相應的 sql + Description: 關於 Master-Detail 的補充說明 + Build Date : 2023/12/16 + Modified for MariaDB +*/ +BEGIN + DECLARE v_maxcount INT; + DECLARE v_currid INT; + DECLARE v_is_md SMALLINT; + DECLARE v_is_excel SMALLINT; + DECLARE v_maxid INT; + DECLARE v_excel_id INT; + DECLARE v_cnt INT; + DECLARE v_cn_field VARCHAR(100); + DECLARE v_cd_field VARCHAR(100); + DECLARE v_billno VARCHAR(20); + DECLARE v_sub_table VARCHAR(100); + DECLARE v_sql TEXT; + DECLARE v_max_no VARCHAR(20); + DECLARE v_maxcnt INT; + + -- step 1: 判斷狀態: 是否 master-detail 多筆資料? + SELECT MAX(parent_id) INTO v_maxcount FROM orm_json_tmp WHERE uuid = p_uuid; + + -- @is_md 是使用 input-table template 用的判斷 + SELECT COUNT(*), IFNULL(MAX(parent_id), 0) + INTO v_is_md, v_maxid + FROM orm_json_tmp + WHERE uuid = p_uuid AND json_key = 'details' AND valuetype = 'array'; + + SELECT COUNT(*), IFNULL(MAX(parent_id), 0) + INTO v_is_excel, v_excel_id + FROM orm_json_tmp + WHERE uuid = p_uuid AND json_key = 'excel' AND valuetype = 'array'; + + -- step 2: update/insert create_user (僅允許一次一筆資料) + SET v_cn_field = 'create_user'; + SET v_cd_field = 'create_date'; + + -- Check if create_user column exists + SELECT COUNT(*) INTO v_cnt + FROM information_schema.columns + WHERE table_name = p_tablename AND column_name = v_cn_field; + + IF v_cnt > 0 THEN + SELECT COUNT(*) INTO v_cnt FROM orm_json_tmp WHERE uuid = p_uuid AND json_key = v_cn_field; + IF v_cnt = 0 THEN + INSERT INTO orm_json_tmp (uuid, parent_id, json_key, json_value, ValueType) + VALUES (p_uuid, v_maxcount, v_cn_field, p_userid, 'string'); + ELSE + UPDATE orm_json_tmp SET json_value = p_userid + WHERE uuid = p_uuid AND json_key = v_cn_field; + END IF; + END IF; + + -- Check if create_date column exists + SELECT COUNT(*) INTO v_cnt + FROM information_schema.columns + WHERE table_name = p_tablename AND column_name = v_cd_field; + + IF v_cnt > 0 THEN + SELECT COUNT(*) INTO v_cnt FROM orm_json_tmp WHERE uuid = p_uuid AND json_key = v_cd_field; + IF v_cnt = 0 THEN + INSERT INTO orm_json_tmp (uuid, parent_id, json_key, json_value, ValueType) + VALUES (p_uuid, v_maxcount, v_cd_field, DATE_FORMAT(NOW(), '%Y-%m-%d'), 'string'); + ELSE + UPDATE orm_json_tmp SET json_value = DATE_FORMAT(NOW(), '%Y-%m-%d') + WHERE uuid = p_uuid AND json_key = v_cd_field; + END IF; + END IF; + + -- step 3: 針對 m-d 的單號前置處理(針對 billno=自動編碼, 給定新的單號) + SELECT COUNT(*) INTO v_cnt + FROM orm_json_tmp + WHERE uuid = p_uuid AND json_key = 'billno' + AND (json_value LIKE '%自動%' OR json_value LIKE '%Auto%'); + + IF v_cnt > 0 THEN + -- hard-code + IF p_pk = '' OR p_tablename LIKE '%_d%' THEN + -- master-detail 的 detail insert + SET v_max_no = CONCAT(SUBSTRING(p_tablename, 4, 6), SUBSTRING(p_userid, 1, 6)); + ELSE + -- 先取得今天最大號 + SET @sql = CONCAT('SELECT MAX(billno) INTO @max_billno FROM ', p_tablename, + ' WHERE DATE(billdate) = CURDATE()'); + PREPARE stmt FROM @sql; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + + -- 計算新單號 (需要實現 sys_next_no 函數) + SET v_max_no = sys_next_no(@max_billno); + + -- 紀錄新增單號 + DELETE FROM sys_new_billno WHERE create_time < DATE_SUB(NOW(), INTERVAL 3 DAY); + + -- 防錯 + SELECT COUNT(*) INTO v_maxcnt + FROM sys_new_billno + WHERE tablename = p_tablename AND login_user = p_userid; + + IF v_maxcnt > 0 THEN + UPDATE sys_new_billno SET billno = v_max_no + WHERE tablename = p_tablename AND login_user = p_userid; + ELSE + INSERT INTO sys_new_billno VALUES (p_tablename, p_userid, v_max_no, NOW()); + END IF; + END IF; + + UPDATE orm_json_tmp SET json_value = v_max_no + WHERE uuid = p_uuid AND json_key = 'billno' + AND (json_value LIKE '%自動%' OR json_value LIKE '%Auto%'); + END IF; + + -- master-detail + IF v_is_md > 0 THEN + -- 寫入 master 一筆 + CALL orm_api_kernel_v2(p_tablename, p_pk, p_action, p_uuid, v_maxcount); + + -- 取得表頭的單號 + SELECT billno INTO v_billno + FROM sys_new_billno + WHERE tablename = p_tablename AND login_user = p_userid; + + -- 寫入 details 多筆 + SELECT detail_table INTO v_sub_table + FROM zen_function + WHERE maintable = p_tablename + LIMIT 1; + + IF IFNULL(v_sub_table, '') = '' THEN + SET v_sub_table = CONCAT(SUBSTRING(p_tablename, 1, LENGTH(p_tablename)-1), 'd'); + END IF; + + -- 多筆 detail 資料寫入 + SET v_currid = 1; + WHILE v_currid < v_maxid DO + -- 執行核心程式 + CALL orm_api_kernel_v2(v_sub_table, p_pk, p_action, p_uuid, v_currid); + + -- 取得下一筆資料 + SET v_currid = v_currid + 1; + END WHILE; + ELSEIF v_is_excel > 0 THEN + -- excel 多筆資料匯入 + SET v_currid = 1; + WHILE v_currid < v_excel_id-1 DO + -- 執行核心程式 + CALL orm_api_kernel_v2(p_tablename, p_pk, p_action, p_uuid, v_currid); + + -- 取得下一筆資料 + SET v_currid = v_currid + 1; + END WHILE; + ELSEIF v_maxcount > 1 THEN + -- 多筆資料寫入 + SET v_currid = 1; + WHILE v_currid <= v_maxcount DO + -- 執行核心程式 + CALL orm_api_kernel_v2(p_tablename, p_pk, p_action, p_uuid, v_currid); + + -- 取得下一筆資料 + SET v_currid = v_currid + 1; + END WHILE; + ELSE + CALL orm_api_kernel_v2(p_tablename, p_pk, p_action, p_uuid, 1); + END IF; +END // + +DELIMITER ; \ No newline at end of file diff --git a/orm_exec_page_v2.txt b/orm_exec_page_v2.txt new file mode 100644 index 0000000..456b598 --- /dev/null +++ b/orm_exec_page_v2.txt @@ -0,0 +1,181 @@ +DELIMITER $$ + +CREATE OR REPLACE PROCEDURE orm_exec_page_v2 ( + IN _tablename VARCHAR(200), + IN _pk VARCHAR(100), + IN _userid VARCHAR(100), + IN _paras TEXT +) +/* + Function : orm_api_v2 [分頁查詢]的核心程式, 智慧的組出相應的 sql, 被 orm_api_v2 呼叫使用 + Description: 要特別注意傳入的參數正確性, 下面是一個正確的 case + call orm_api_v2('b013a91a-daf1-11f0-a07a-d8bbc15c1404','basperson','personid','P','1^100^personid^*^^^personcname^林^^'); + Build Date : 2025/07/11 + + Modify History + Item Who Date Modify Docs + ==== ========== ========== ================================================ + 1 Michael 2025/07/11 根據 ms-sql 的版本, 利用 ChatGPT/DeepSeek 等AI, 改寫為 Mariadb 版 + +*/ +BEGIN + DECLARE _pageno INT DEFAULT 1; + DECLARE _pagerec INT DEFAULT 10; + DECLARE _orderby TEXT DEFAULT ''; + DECLARE _udf_fields TEXT DEFAULT '*'; + DECLARE _wheresql TEXT DEFAULT ''; + DECLARE _wheresql_org TEXT DEFAULT ''; + DECLARE _where_value TEXT DEFAULT ''; + DECLARE _where_fields TEXT DEFAULT ''; + DECLARE _where_field TEXT DEFAULT ''; + DECLARE _where_idvalue TEXT DEFAULT ''; + DECLARE _menuid TEXT DEFAULT ''; + DECLARE _jointables TEXT DEFAULT ''; + DECLARE _joinfields TEXT DEFAULT ''; + DECLARE _tbl_alias TEXT DEFAULT ''; + DECLARE _alias TEXT DEFAULT ''; + DECLARE _startrec INT DEFAULT 0; + DECLARE _sql TEXT; + DECLARE _sql_cnt TEXT; + DECLARE _dtc INT DEFAULT 0; + DECLARE _guid VARCHAR(100); + -- + DECLARE v_field VARCHAR(200); + DECLARE v_pos INT DEFAULT 1; + DECLARE v_count INT; + + -- 錯誤處理 + /* + DECLARE CONTINUE HANDLER FOR SQLEXCEPTION + BEGIN + SELECT '9999' AS code, '資料庫操作錯誤' AS msg; + END; + */ + + DECLARE CONTINUE HANDLER FOR SQLEXCEPTION + BEGIN + GET DIAGNOSTICS CONDITION 1 + @sqlstate = RETURNED_SQLSTATE, + @errno = MYSQL_ERRNO, + @text = MESSAGE_TEXT; + + SELECT 1 AS code, CONCAT('Error: ', @errno, ' (', @sqlstate, '): ', @text) AS msg; + END; + + SET _guid = UUID(); + CALL gp_split(_guid, _paras, '^'); + + -- 使用 split 暫存表方式處理參數 + SELECT col INTO _pageno FROM _split WHERE guid = _guid and rowid = 1; + SELECT col INTO _pagerec FROM _split WHERE guid = _guid and rowid = 2; + SELECT col INTO _orderby FROM _split WHERE guid = _guid and rowid = 3; + SELECT col INTO _udf_fields FROM _split WHERE guid = _guid and rowid = 4; + SELECT col INTO _wheresql_org FROM _split WHERE guid = _guid and rowid = 5; + SELECT col INTO _menuid FROM _split WHERE guid = _guid and rowid = 6; + SELECT col INTO _where_fields FROM _split WHERE guid = _guid and rowid = 7; + SELECT col INTO _where_value FROM _split WHERE guid = _guid and rowid = 8; + SELECT col INTO _where_field FROM _split WHERE guid = _guid and rowid = 9; + SELECT col INTO _where_idvalue FROM _split WHERE guid = _guid and rowid = 10; + + DELETE FROM _split WHERE guid = _guid; + + -- 預設排序欄位 + IF _orderby IS NULL OR _orderby = '' THEN SET _orderby = _pk; END IF; + IF _udf_fields IS NULL OR _udf_fields = '' THEN SET _udf_fields = '*'; END IF; + + -- 防錯 + SET _jointables = ''; + IF _menuid IS NOT NULL AND _menuid <> '' THEN + SELECT joinfields, jointables INTO _joinfields, _jointables + FROM sys_joinfields WHERE menuid = _menuid; + + SET _alias = 'ma.'; + SET _tbl_alias = ' ma '; + IF LOCATE('ma.', _orderby) = 0 THEN + SET _orderby = CONCAT(_alias, _orderby); + END IF; + END IF; + + -- 建立 WHERE 條件 + IF _where_value IS NOT NULL AND _where_value <> '' THEN + IF LOCATE(',', _where_fields) > 0 THEN + -- SET _wheresql = REPLACE(_where_fields, ',', CONCAT(' LIKE ',"'",'%', _where_value, '%', "'", ' OR ', _alias)); + -- SET _wheresql = CONCAT(' WHERE (', _alias, _where_fields, ' LIKE ', QUOTE(CONCAT('%', _where_value, '%')), ')'); + + -- 改寫, 本段程式目的是要多欄位 like 查詢 + SET v_count = LENGTH(_where_fields) - LENGTH(REPLACE(_where_fields, ',', '')) + 1; + WHILE v_pos <= v_count DO + SET v_field = TRIM( + SUBSTRING_INDEX( + SUBSTRING_INDEX(_where_fields, ',', v_pos), + ',', -1 + ) + ); + + SET _wheresql = CONCAT( + _wheresql, + IF(v_pos > 1, ' OR ', ''), + v_field, + ' LIKE ', + QUOTE(CONCAT('%', _where_value, '%')) + ); + + SET v_pos = v_pos + 1; + END WHILE; + SET _wheresql = CONCAT(' WHERE (', _wheresql, ')'); + -- + ELSE + SET _wheresql = CONCAT(' WHERE ', _alias, _where_fields, ' LIKE ', QUOTE(CONCAT('%', _where_value, '%'))); + END IF; + END IF; + -- debug + -- select concat('where9: ',_wheresql); + + -- 加入網址參數條件 + IF _where_idvalue IS NOT NULL AND _where_idvalue <> '' THEN + IF _wheresql = '' THEN + SET _wheresql = CONCAT(' WHERE ', _alias, _where_field, ' = ', QUOTE(_where_idvalue)); + ELSE + SET _wheresql = CONCAT(_wheresql, ' AND ', _alias, _where_field, ' = ', QUOTE(_where_idvalue)); + END IF; + END IF; + + -- 加上前端傳入的原始 WHERE + IF _wheresql_org IS NOT NULL AND _wheresql_org <> '' THEN + SET _wheresql_org = REPLACE(_wheresql_org, '~', "'"); + IF _wheresql = '' THEN + SET _wheresql = CONCAT(' WHERE ', _wheresql_org); + ELSE + SET _wheresql = CONCAT(_wheresql, ' AND ', _wheresql_org); + END IF; + END IF; + + -- 計算 offset + SET _startrec = (_pageno - 1) * _pagerec; + + -- 查詢筆數 + SET _sql_cnt = CONCAT('SELECT COUNT(*) INTO @dtc FROM ', _tablename, _tbl_alias, ' ', _wheresql); + PREPARE stmt_cnt FROM _sql_cnt; + EXECUTE stmt_cnt; + DEALLOCATE PREPARE stmt_cnt; + + SELECT 0 AS code, 'ok' AS msg, @dtc AS recno; + -- SELECT 'debug' AS code, _sql_cnt AS msg, @dtc AS recno; + + -- 組合查詢 SQL + IF _jointables IS NULL OR _jointables = '' THEN + SET _sql = CONCAT('SELECT ', _udf_fields, ' FROM ', _tablename, _tbl_alias, ' ', _wheresql, + ' ORDER BY ', _orderby, ' LIMIT ', _pagerec, ' OFFSET ', _startrec); + ELSE + SET _sql = CONCAT('SELECT ', _joinfields, ' FROM ', _tablename, _tbl_alias, _jointables, ' ', + _wheresql, ' ORDER BY ', _orderby, ' LIMIT ', _pagerec, ' OFFSET ', _startrec); + END IF; + + -- 執行查詢 + PREPARE stmt FROM _sql; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + +END$$ + +DELIMITER ; diff --git a/orm_exec_update_v2.txt b/orm_exec_update_v2.txt new file mode 100644 index 0000000..5006c0f --- /dev/null +++ b/orm_exec_update_v2.txt @@ -0,0 +1,99 @@ +DELIMITER // + +CREATE OR REPLACE PROCEDURE orm_exec_update_v2( + IN p_tablename VARCHAR(200), -- 表名 + IN p_pk VARCHAR(100), -- 主键(用于更新/删除) + IN p_action VARCHAR(20), -- CRUDP操作 + IN p_uuid VARCHAR(100), -- UUID标识 + IN p_userid VARCHAR(100) -- 用户ID +) +/* + 功能: orm_api_v2 [更新]的核心程序,智能构建相应SQL + 描述: 用于更新操作的ORM核心逻辑 + 创建日期: 2023/12/16 + MariaDB转换版本 +*/ +BEGIN + DECLARE v_maxcount, v_rec INT; + DECLARE v_currid INT; + DECLARE v_cnt INT; + DECLARE v_un_field VARCHAR(100); + DECLARE v_ud_field VARCHAR(100); + + -- 步骤1: 获取最小parent_id (rows) + SELECT MIN(parent_id) INTO v_maxcount + FROM orm_json_tmp + WHERE uuid = p_uuid; -- AND valuetype in ('ARRAY','object'); + + -- 取得欲更新筆數 + SELECT MAX(object_id) INTO v_rec + FROM orm_json_tmp + WHERE uuid = p_uuid AND parent_id = v_maxcount; + + -- 步骤2: 更新/插入update_user和update_date字段 + SET v_un_field = 'update_user'; + SET v_ud_field = 'update_date'; + + -- 检查update_user字段是否存在 + SELECT COUNT(*) INTO v_cnt + FROM information_schema.columns + WHERE table_name = p_tablename AND column_name = v_un_field; + + IF v_cnt > 0 THEN + SELECT COUNT(*) INTO v_cnt + FROM orm_json_tmp + WHERE uuid = p_uuid AND json_key = v_un_field; + + IF v_cnt = 0 THEN + INSERT INTO orm_json_tmp (uuid, parent_id, json_key, json_value, ValueType) + VALUES (p_uuid, v_maxcount, v_un_field, p_userid, 'string'); + ELSE + UPDATE orm_json_tmp + SET json_value = p_userid + WHERE uuid = p_uuid AND json_key = v_un_field; + END IF; + END IF; + + -- 检查update_date字段是否存在 + SELECT COUNT(*) INTO v_cnt + FROM information_schema.columns + WHERE table_name = p_tablename AND column_name = v_ud_field; + + IF v_cnt > 0 THEN + SELECT COUNT(*) INTO v_cnt + FROM orm_json_tmp + WHERE uuid = p_uuid AND json_key = v_ud_field; + + IF v_cnt = 0 THEN + INSERT INTO orm_json_tmp (uuid, parent_id, json_key, json_value, ValueType) + VALUES (p_uuid, v_maxcount, v_ud_field, DATE_FORMAT(NOW(), '%Y-%m-%d'), 'string'); + ELSE + UPDATE orm_json_tmp + SET json_value = DATE_FORMAT(NOW(), '%Y-%m-%d') + WHERE uuid = p_uuid AND json_key = v_ud_field; + END IF; + END IF; + + -- 日期欄位要格式化處理(待驗證) + UPDATE orm_json_tmp + SET json_value = DATE_FORMAT(substr(json_value,1,10), '%Y-%m-%d') + WHERE uuid = p_uuid AND json_key in (select column_name from information_schema.columns WHERE table_name = p_tablename and substr(data_type,1,4) = 'date') + and json_value is not null; + + -- 判断是否有多笔资料 + IF v_rec > 1 THEN -- v_maxcount + SET v_currid = 1; + WHILE v_currid <= v_rec DO + -- 执行核心程序 + CALL orm_api_kernel_v2(p_tablename, p_pk, p_action, p_uuid, v_maxcount*1000 + v_currid); + + -- 取得下一笔资料 + SET v_currid = v_currid + 1; + END WHILE; + ELSE + -- 單筆資料傳入 + CALL orm_api_kernel_v2(p_tablename, p_pk, p_action, p_uuid, 1); + END IF; +END // + +DELIMITER ; \ No newline at end of file diff --git a/orm_page_where_v2.txt b/orm_page_where_v2.txt new file mode 100644 index 0000000..3f52531 --- /dev/null +++ b/orm_page_where_v2.txt @@ -0,0 +1,131 @@ +DELIMITER // + +DROP FUNCTION IF EXISTS orm_page_where_v2; +CREATE FUNCTION orm_page_where_v2( + tablename VARCHAR(200), -- 表名 + pk VARCHAR(100), -- 主键(用于更新删除) + where_sql TEXT, -- 原始 WHERE 条件 + userid VARCHAR(100) -- 用户ID +) +RETURNS TEXT +DETERMINISTIC +/* +Function : orm_api_v2 [分頁查詢] where 條件判斷的核心程式, 智慧的組出相應的 sql, 被 orm_api_page_v2 呼叫使用 +Description: +Build Date : 2024-06-20 +Modify History: +1 Michael 2023/12/16 原始版本 +2 [优化版] 2024-06-20 MariaDB优化版本 +*/ +BEGIN + DECLARE wheresql TEXT; + DECLARE limit_s_date VARCHAR(100); + DECLARE limit_e_date VARCHAR(100); + DECLARE limit_field VARCHAR(100); + DECLARE limit_field_d VARCHAR(100); + DECLARE limit_filter_master TEXT; + DECLARE limit_table_d VARCHAR(1000); + DECLARE limit_filter_detail TEXT; + DECLARE limit_filter_date VARCHAR(200); + DECLARE maxcount INT DEFAULT 0; + DECLARE new_billno VARCHAR(100); + DECLARE comma_pos INT; + DECLARE emp1, emp2, emp3 VARCHAR(200); + + -- 初始化 WHERE 子句 + SET wheresql = IF(where_sql = '', ' WHERE 1=1 ', where_sql); + + -- 获取高级查询限制配置 + SELECT + limit_field, limit_table_d, limit_field_d, limit_s_date, limit_e_date + INTO + limit_field, limit_table_d, limit_field_d, limit_s_date, limit_e_date + FROM sys_menutable + WHERE limit_table = tablename + AND islimitdata = 'Y' + LIMIT 1; + + -- 如果存在限制字段且用户已登录 + IF limit_field IS NOT NULL AND userid <> '' THEN + -- 检查是否有全局权限 (*) + SELECT COUNT(*) INTO maxcount + FROM basperson_share + WHERE personid = userid + AND share_personid = '*'; + + -- 如果没有全局权限,则添加个人限制条件 + IF maxcount = 0 THEN + -- 初始化过滤条件 + SET limit_filter_master = ''; + SET limit_filter_detail = ''; + SET limit_filter_date = ''; + + -- 添加日期范围限制 + IF limit_s_date <> '' AND limit_e_date <> '' THEN + SET limit_filter_date = CONCAT( + ' AND ', limit_s_date, ' <= CURDATE()', + ' AND ', limit_e_date, ' >= CURDATE()' + ); + END IF; + + -- 添加明细表限制 + IF limit_field_d <> '' THEN + SET limit_filter_detail = CONCAT( + ' OR ', pk, ' IN (', + 'SELECT ', pk, ' FROM ', limit_table_d, + ' WHERE ', limit_field_d, ' = ', QUOTE(userid), + limit_filter_date, ')' + ); + END IF; + + -- 处理多字段限制 + SET comma_pos = LOCATE(',', limit_field); + IF comma_pos > 0 THEN + -- 拆分字段列表 + SET emp1 = SUBSTRING_INDEX(limit_field, ',', 1); + SET emp2 = SUBSTRING_INDEX(SUBSTRING_INDEX(limit_field, ',', 2), ',', -1); + SET emp3 = IF(LOCATE(',', limit_field, LENGTH(emp1) + LENGTH(emp2) + 3) > 0, + SUBSTRING_INDEX(limit_field, ',', -1), + NULL); + + -- 构建多字段限制条件 + SET wheresql = CONCAT(wheresql, ' AND ('); + SET wheresql = CONCAT(wheresql, emp1, ' IN (SELECT share_personid FROM basperson_share WHERE personid = ', QUOTE(userid), ')'); + + IF emp2 <> '' THEN + SET wheresql = CONCAT(wheresql, ' OR ', emp2, ' IN (SELECT share_personid FROM basperson_share WHERE personid = ', QUOTE(userid), ')'); + END IF; + + IF emp3 IS NOT NULL THEN + SET wheresql = CONCAT(wheresql, ' OR ', emp3, ' IN (SELECT share_personid FROM basperson_share WHERE personid = ', QUOTE(userid), ')'); + END IF; + + SET wheresql = CONCAT(wheresql, limit_filter_date, ')', limit_filter_detail); + ELSE + -- 单字段限制条件 + SET limit_filter_master = CONCAT( + ' AND ', limit_field, ' IN (', + 'SELECT share_personid FROM basperson_share WHERE personid = ', QUOTE(userid), ')', + limit_filter_date + ); + SET wheresql = CONCAT(wheresql, limit_filter_master, limit_filter_detail); + END IF; + END IF; + END IF; + + /* 新增记录后只显示刚添加的数据 */ + SELECT billno INTO new_billno + FROM sys_new_billno + WHERE tablename = tablename + AND login_user = userid + AND create_time >= NOW() - INTERVAL 30 SECOND + LIMIT 1; + + IF new_billno IS NOT NULL THEN + SET wheresql = CONCAT(wheresql, ' AND billno = ', QUOTE(new_billno)); + END IF; + + RETURN wheresql; +END // + +DELIMITER ; \ No newline at end of file diff --git a/orm_retrive_json_v2.txt b/orm_retrive_json_v2.txt new file mode 100644 index 0000000..7c92129 --- /dev/null +++ b/orm_retrive_json_v2.txt @@ -0,0 +1,64 @@ +DELIMITER // + +CREATE OR REPLACE FUNCTION orm_retrive_json_v2( + paras TEXT +) +RETURNS TEXT +DETERMINISTIC +/* + Function : orm_api_v2 [傳入 json 的資料預處理] 的核心程式, 被 orm_api_v2 呼叫使用 + Description: 刪除 @para 多餘的內容(amis) + 如果有特殊元件(如 transfer) 會多 POST 非 orm 所需的資料, 一樣要拿掉 + picker 如果有設 joinValues:false, 也會多傳一些多餘的資料 + Build Date : 2024-06-20 + Modify History: + 1 Michael 2023/12/16 原始版本 + 2 Michael 2024-06-20 改寫為MariaDB版本並優化 +*/ +BEGIN + DECLARE result TEXT; + DECLARE maxcount INT; + DECLARE currid INT; + DECLARE trans_data TEXT; + DECLARE json_valid BOOLEAN; + + -- 檢查是否為有效JSON,如果是則直接處理,否則按原邏輯處理 + SET json_valid = JSON_VALID(paras); + + -- 進行一些操作,根據需求修改下面的程式碼 + SET result = paras; + + -- 處理 config 部分 + SET maxcount = INSTR(result, '"config":['); + IF maxcount > 0 THEN + -- 在 config 後可能還有資料 + SET trans_data = SUBSTRING(result FROM maxcount); + SET currid = INSTR(trans_data, '],'); + SET trans_data = SUBSTRING(trans_data FROM currid + 2); + + SET result = CONCAT(SUBSTRING(result FROM 1 FOR maxcount - 1), ' ', trans_data); + END IF; + + -- 處理 token 部分(除非是電子書情況) + SET maxcount = INSTR(result, '"token"'); + IF maxcount > 0 THEN + SET result = CONCAT(SUBSTRING(result FROM 1 FOR maxcount - 2), '}'); + END IF; + + -- 處理換行符 + IF INSTR(result, '\\n') > 0 THEN + SET result = REPLACE(REPLACE(result, '\\r\\n', CHAR(10)), '\\n', CHAR(10)); + END IF; + + -- 如果是有效JSON,最後再驗證一次 + IF json_valid THEN + IF NOT JSON_VALID(result) THEN + -- 如果處理後變無效,返回原始值 + RETURN paras; + END IF; + END IF; + + RETURN result; +END // + +DELIMITER ; \ No newline at end of file diff --git a/parse_JSON.txt b/parse_JSON.txt new file mode 100644 index 0000000..ead6abe --- /dev/null +++ b/parse_JSON.txt @@ -0,0 +1,168 @@ +DELIMITER // + +CREATE OR REPLACE PROCEDURE Parse_JSON( + IN p_uuid VARCHAR(100), + IN json_input JSON +) +/* +call Parse_JSON('uuid-20250803', +'{"custcateid":"08","custcatecname":"8888","custcateename":"","memochar":"","mergetype":"","token":"c975931c-6f8d-11f0-9c5f-d8bbc15c1404","_$_tableName":"bascustcategory", +"_$_pk":"custcateid", +"_$_action":"D"}'); + +call Parse_JSON('uuid-20250803', +'{"sprintid":"Next-Sprint","sprint_title":"調整現行 PBI 緩衝用的 Sprint(2025)","sprint_startdate":"2025-01-01T00:00:00.000+08:00","sprint_enddate":"2099-12-30T16:00:00.000Z","time_range":"2099", +"token":"768f1c89-6fb9-11f0-9c5f-d8bbc15c1404", +"_$_tableName":"pms_sprint", +"_$_pk":"sprintid", +"_$_action":"U"}'); + +call Parse_JSON('uuid-20250803_3', +'{"rows":[{"custcateid":"09","custcatecname":"9999","custcateename":"9999-memo","memochar":"","mergetype":""}], +"rowsDiff":[{"custcateename":"9999-memo"}],"indexes":["7"], +"rowsOrigin": +[{"custcateid":"09","custcatecname":"9999","custcateename":"","memochar":"","mergetype":""}], +"unModifiedItems":[ +{"custcateid":"01","custcatecname":"經銷商","custcateename":"333","memochar":"","mergetype":"0"}, +{"custcateid":"02","custcatecname":"代理商","custcateename":"444","memochar":"","mergetype":"0"}, +{"custcateid":"03","custcatecname":"大型企業","custcateename":"","memochar":"","mergetype":"0"}, +{"custcateid":"04","custcatecname":"政府","custcateename":"","memochar":"","mergetype":"0"}, +{"custcateid":"05","custcatecname":"中小企業","custcateename":"","memochar":"","mergetype":"0"}, +{"custcateid":"06","custcatecname":"學校","custcateename":"","memochar":"","mergetype":"0"}, +{"custcateid":"07","custcatecname":"竹科","custcateename":"","memochar":"","mergetype":"0"}], +"token":"d101363e-700c-11f0-9c5f-d8bbc15c1404","_$_tableName":"bascustcategory","_$_pk":"custcateid","_$_action":"U"}'); + +call Parse_JSON('uuid-20250820_1', +'{"projectclassid":"soft-dev","proj_boss":"120102", +"config":[{"label":"(GM) 總經理室", +"children":[ +{"label":"(120101) 高光","value":"120101"}, +{"label":"(121108) 沈秋月","value":"121108"}, +{"label":"(200605) 陳明文","value":"200605"}, +{"label":"(120102) 王俊傑","value":"120102"}, +{"label":"(150201) 衛斯理","value":"150201"}, +{"label":"(190802) 葉子龍","value":"190802"}, +{"label":"(210301) 高國華","value":"210301"}, +{"label":"(admin)admin","value":"admin"}, +{"label":"(160805) 邱雅惠","value":"160805"}, +{"label":"(170911) 劉強南","value":"170911"}, +{"label":"(201001) 林志堅","value":"201001"}, +{"label":"(120912) 陳進德","value":"120912"}, +{"label":"(170109) 林玉玲","value":"170109"}, +{"label":"(180210) 秦一明","value":"180210"}, +{"label":"(191006) 歐陽龍","value":"191006"}, +{"label":"(201036) 邱郁芳","value":"201036"}, +{"label":"(demo) 測試用戶","value":"demo"}] +}], +"projectid":"IT-2025-Chaos","proj_cname":"MariaDB改寫專案","proj_status":"SASD","proj_startdate":"2025-08-01","proj_enddate":"2025-09-30", +"proj_year":"2025","token":"a85afca2-7dc5-11f0-9c5f-d8bbc15c1404", +"_$_tableName":"pmsproject", +"_$_pk":"projectid", +"_$_action":"C"}'); + + +*/ +BEGIN + DECLARE i INT DEFAULT 0; + DECLARE total_keys INT; + DECLARE key_name VARCHAR(255); + DECLARE val JSON; + + DECLARE sub_key_name VARCHAR(255); + DECLARE sub_val JSON; + DECLARE sub_total_keys INT; + DECLARE j INT; + DECLARE x INT; + DECLARE y INT; + DECLARE arr_count,arr_idx INT; + DECLARE arr_obj JSON; + + -- 取第一層 keys 數量 + SET total_keys = JSON_LENGTH(JSON_KEYS(json_input)); + SET x = 1; + SET y = 1; + + WHILE i < total_keys DO + SET key_name = JSON_UNQUOTE(JSON_EXTRACT(JSON_KEYS(json_input), CONCAT('$[', i, ']'))); + SET val = JSON_EXTRACT(json_input, CONCAT('$.', key_name)); + + -- 插入第一層 + -- INSERT INTO tmp_json_parse_result (level, parent_key, key_name, value) + INSERT INTO sys_jsondata (UUID, `NAME`, SequenceNo, parent_ID, StringValue, ValueType) + VALUES (p_uuid, key_name, x, NULL, JSON_UNQUOTE(val), JSON_TYPE(val)); + + SET y = x; + SET x = x + 1; + + -- 第二層處理:若 value 是 object 或 array,解析其 key-value (當前版本只處理 2層, 多的暫時不管) + IF JSON_TYPE(val) IN ('OBJECT', 'ARRAY') THEN + -- 先處理 ARRAY 可能有多筆資料 + IF JSON_TYPE(val) = 'ARRAY' THEN + SET arr_count = JSON_LENGTH(val); + SET arr_idx = 0; + + WHILE arr_idx < arr_count DO + -- 取出陣列中的單一物件 + SET arr_obj = JSON_EXTRACT(val, CONCAT('$[', arr_idx, ']')); + SET j = 0; + SET sub_total_keys = JSON_LENGTH(JSON_KEYS(arr_obj)); + + WHILE j < sub_total_keys DO + SET sub_key_name = JSON_UNQUOTE(JSON_EXTRACT(JSON_KEYS(arr_obj), CONCAT('$[', j, ']'))); + SET sub_val = JSON_EXTRACT(arr_obj, CONCAT('$.', sub_key_name)); + + INSERT INTO sys_jsondata (UUID, `NAME`, SequenceNo, parent_ID, Object_ID, StringValue, ValueType) + VALUES (p_uuid, sub_key_name, x, y, arr_idx+1, JSON_UNQUOTE(sub_val), JSON_TYPE(sub_val)); + + SET j = j + 1; + SET x = x + 1; + END WHILE; + + SET arr_idx = arr_idx + 1; + END WHILE; + ELSE + -- 處理單一 OBJECT + SET j = 0; + SET sub_total_keys = JSON_LENGTH(JSON_KEYS(val)); + + WHILE j < sub_total_keys DO + SET sub_key_name = JSON_UNQUOTE(JSON_EXTRACT(JSON_KEYS(val), CONCAT('$[', j, ']'))); + SET sub_val = JSON_EXTRACT(val, CONCAT('$.', sub_key_name)); + + INSERT INTO sys_jsondata (UUID, `NAME`, SequenceNo, parent_ID, StringValue, ValueType) + VALUES (p_uuid, sub_key_name, x, y, JSON_UNQUOTE(sub_val), JSON_TYPE(sub_val)); + + SET j = j + 1; + SET x = x + 1; + END WHILE; + END IF; + END IF; + + /* + -- 第二層處理:若 value 是 object,解析其 key-value + IF JSON_TYPE(val) in ('OBJECT','ARRAY') THEN + -- 先解決 rows Grid Edit 多筆資料, 後續應該還有其他狀況要一一排除 + SET j = 0; + -- IF SELECT TRIM(BOTH '[]' FROM '[example]') AS result; + SET sub_total_keys = JSON_LENGTH(JSON_KEYS(val)); + + WHILE j < sub_total_keys DO + SET sub_key_name = JSON_UNQUOTE(JSON_EXTRACT(JSON_KEYS(val), CONCAT('$[', j, ']'))); + SET sub_val = JSON_EXTRACT(val, CONCAT('$.', sub_key_name)); + + -- INSERT INTO tmp_json_parse_result (level, parent_key, key_name, value) + INSERT INTO sys_jsondata (UUID, `NAME`, SequenceNo, parent_ID, StringValue, ValueType) + VALUES (p_uuid, sub_key_name, x, y , JSON_UNQUOTE(sub_val), JSON_TYPE(val)); + + SET j = j + 1; + SET x = x + 1; + END WHILE; + END IF; + */ + + SET i = i + 1; + END WHILE; + +END // + +DELIMITER ; diff --git a/usp_parsejson.txt b/usp_parsejson.txt new file mode 100644 index 0000000..1b4a8d4 --- /dev/null +++ b/usp_parsejson.txt @@ -0,0 +1,70 @@ +DELIMITER $$ + +CREATE OR REPLACE PROCEDURE usp_parsejson ( + IN _uuid VARCHAR(100), + IN _json TEXT +) +/* + CALL usp_parsejson('1234', '{ "id":"abc", "nmae": "Michael"}'); + SELECT * from _jsondata WHERE guid = '1234'; + + DELETE FROM _jsondata WHERE guid = '1234'; +*/ +BEGIN + DECLARE _json_clean TEXT; + DECLARE _pair TEXT; + DECLARE _key VARCHAR(255); + DECLARE _value TEXT; + DECLARE _valuetype VARCHAR(20); + DECLARE _pos INT DEFAULT 1; + DECLARE _len INT; + DECLARE _curr INT DEFAULT 1; + DECLARE _next INT; + DECLARE _cnt INT DEFAULT 1; + + -- 建立暫存表 + CREATE TEMPORARY TABLE IF NOT EXISTS _jsondata ( + guid VARCHAR(100), + rowid INT, + fieldname VARCHAR(255), + contents TEXT, + fieldtype VARCHAR(20) + ); + + -- 清除左右大括號與空白 + SET _json_clean = REPLACE(_json,'{',''); + SET _json_clean = REPLACE(_json_clean,'}',''); + + -- 計算逗號數(也就是 key:value 數) + SET _len = LENGTH(_json_clean) - LENGTH(REPLACE(_json_clean, ',', '')) + 1; + + WHILE _cnt <= _len DO + -- 取出第 cnt 個 key:value 配對 + SET _pair = SUBSTRING_INDEX(SUBSTRING_INDEX(_json_clean, ',', _cnt), ',', -1); + + -- 取出 key + SET _key = SUBSTRING_INDEX(_pair, ':', 1); + SET _key = REPLACE(TRIM(_key),'"',''); + + -- 取出 value + SET _value = SUBSTRING_INDEX(_pair, ':', -1); + SET _value = TRIM(_value); + + -- 判斷型別 + IF LEFT(_value, 1) = '"' AND RIGHT(_value, 1) = '"' THEN + SET _valuetype = 'string'; + SET _value = REPLACE(_value,'"',''); + ELSE + SET _valuetype = 'number'; + END IF; + + -- 寫入結果 + INSERT INTO _jsondata (guid, rowid, fieldname, contents, fieldtype) + VALUES (_uuid, _cnt, _key, _value, _valuetype); + + -- 下一筆 + SET _cnt = _cnt + 1; + END WHILE; +END$$ + +DELIMITER ;