我是 Oracle SQL PL/SQL 的新手,所以请多多包涵。我希望能够通过动态构建动态 SQL 语句并执行它们来修改/更新具有来自另一个数据集 (tblMODIFY_TEST) 的特定数据值的数据集 (tblDATA_TEST)。
到目前为止,我构建的示例基于使用 CURSOR (FOR/LOOP),但昨天我看到一篇文章建议更合适的解决方案是使用 REFCURSOR,但遗憾的是没有丰富的 Oracle 经验语法 我想我最好在迷路之前问问专家们。
使用示例数据集的预期最终结果将是看到以下构造的 SQL 语句正在创建和执行。
-- Example constucted SQL statments
-- UPDATE "tblDATA_TEST" SET "tb_excl_flg" = -1 WHERE "BR_CD" = '0123';
-- UPDATE "tblDATA_TEST" SET "is_hdgd_flg" = -1 WHERE "ORG_GRP_ID" = 654;
-- UPDATE "tblDATA_TEST" SET "data_dte" = TO_DATE('31.10.2020','DD.MM.YYYY') WHERE "data_dte" = TO_DATE('3.09.2020','DD.MM.YYYY');
-- UPDATE "tblDATA_TEST" SET "is_incl_flg" = -1 WHERE "data_dte" = TO_DATE('31.10.2020','DD.MM.YYYY');
-- UPDATE "tblDATA_TEST" SET "OUT_AMT" = 800.50 WHERE "ORG_ID" = 321;
-- UPDATE "tblDATA_TEST" SET "OUT_AMT" = 500.50 WHERE "ORG_DESCR" = 'CLIENT E';
-- UPDATE "tblDATA_TEST" SET "OUT_AMT" = 800.50 WHERE "CTRY_CD" = 'UK' AND "BR_CD" = '0654' AND "ORG_ID" = 888;
下面是示例代码,假设您在名为“tblDATA_TEST”的表中保存了以下数据集,其中插入了以下示例数据记录。
CREATE TABLE "tblDATA_TEST" ("data_dte" DATE, RGN_CD VARCHAR2(5), CTRY_CD VARCHAR2(5), BR_CD VARCHAR2(5), ORG_GRP_ID NUMBER, ORG_ID NUMBER, ORG_DESCR VARCHAR2(255), OUT_AMT FLOAT, "is_ovrdue_flg" NUMBER, "is_hdgd_flg" NUMBER, "tb_incl_flg" NUMBER, "tb_excl_flg" NUMBER);
INSERT INTO "tblDATA_TEST" VALUES (TO_DATE('30.09.2020','DD.MM.YYYY'), 'AMER', 'US', 0123, 987, 789, 'CLIENT A', 100.50, 0, 0, 0, 0 );
INSERT INTO "tblDATA_TEST" VALUES (TO_DATE('30.09.2020','DD.MM.YYYY'), 'AMER', 'US', 0123, 654, 456, 'CLIENT B', 200.50, 0, 0, 0, 0 );
INSERT INTO "tblDATA_TEST" VALUES (TO_DATE('30.09.2020','DD.MM.YYYY'), 'EMEA', 'UK', 0456, 321, 123, 'CLIENT C', 300.50, 0, 0, 0, 0 );
INSERT INTO "tblDATA_TEST" VALUES (TO_DATE('30.09.2020','DD.MM.YYYY'), 'EMEA', 'UK', 0456, 654, 654, 'CLIENT D', 400.50, 0, 0, 0, 0 );
INSERT INTO "tblDATA_TEST" VALUES (TO_DATE('30.09.2020','DD.MM.YYYY'), 'EMEA', 'UK', 0654, 321, 321, 'CLIENT E', 500.50, 0, 0, 0, 0 );
INSERT INTO "tblDATA_TEST" VALUES (TO_DATE('30.09.2020','DD.MM.YYYY'), 'EMEA', 'UK', 0654, 321, 888, 'CLIENT F', 600.50, 0, 0, 0, 0 );
并且我希望能够通过以下修改数据表 tblMODIFY_TEST 和插入的数据记录来更新 tblDATA_TEST 表中的一些数据记录。
CREATE TABLE "tblMODIFY_TEST" ("data_dte" DATE, "mdfy_rank" NUMBER, "tbl_name" VARCHAR2(250), "fld_name" VARCHAR2(250), "fld_value" VARCHAR2(250), "reason_descr" VARCHAR2(250), "valid_dte" DATE, "criteriafld1" VARCHAR2(250 BYTE), "criteriavalue1" VARCHAR2(250), "criteriafld2" VARCHAR2(250), "criteriavalue2" VARCHAR2(250), "criteriafld3" VARCHAR2(250), "criteriavalue3" VARCHAR2(250), "criteriafld4" VARCHAR2(250), "criteriavalue4" VARCHAR2(250), "criteriafld5" VARCHAR2(250), "criteriavalue5" VARCHAR2(250));
INSERT INTO "tblMODIFY_TEST" VALUES (TO_DATE('31.10.2020','DD.MM.YYYY'), '1', 'tblDATA_TEST', 'tb_excl_flg', '-1', 'Test branch code (BR_CD), varchar data type', NULL ,'BR_CD', '0123', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
INSERT INTO "tblMODIFY_TEST" VALUES (TO_DATE('31.10.2020','DD.MM.YYYY'), '2', 'tblDATA_TEST', 'is_hdgd_flg', '-1', 'Test organisation group code (ORG_GRP_CD), number data type', NULL, 'ORG_GRP_CD', '654', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
INSERT INTO "tblMODIFY_TEST" VALUES (TO_DATE('31.10.2020','DD.MM.YYYY'), '3', 'tblDATA_TEST', 'data_dte', '31.10.2020', 'Test data date (date_dte), date data type', NULL, 'data_dte', '30.09.2020', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
INSERT INTO "tblMODIFY_TEST" VALUES (TO_DATE('31.10.2020','DD.MM.YYYY'), '4', 'tblDATA_TEST', 'is_incl_flg', '-1', 'Test data date (date_dte), date data type', NULL, 'data_dte', '31.10.2020', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
INSERT INTO "tblMODIFY_TEST" VALUES (TO_DATE('31.10.2020','DD.MM.YYYY'), '5', 'tblDATA_TEST', 'OUT_AMT', '800.50', 'Test outstanding amount (OUT_AMT), float data type', NULL, 'ORG_ID', '321', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
INSERT INTO "tblMODIFY_TEST" VALUES (TO_DATE('31.10.2020','DD.MM.YYYY'), '6', 'tblDATA_TEST', 'OUT_AMT', '800.50', 'Test validation date, this should not get processed (valid_dte)', TO_DATE('31.08.2020','DD.MM.YYYY'), 'ORG_ID', '321', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
INSERT INTO "tblMODIFY_TEST" VALUES (TO_DATE('31.10.2020','DD.MM.YYYY'), '7', 'tblDATA_TEST', 'OUT_AMT', '500.50', 'Test outstanding amount (OUT_AMT), float data type', NULL, 'ORG_DESCR', 'CLIENT E', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
INSERT INTO "tblMODIFY_TEST" VALUES (TO_DATE('31.10.2020','DD.MM.YYYY'), '8', 'tblDATA_TEST', 'OUT_AMT', '800.50', 'Test outstanding amount (OUT_AMT), float data type and multiple criteria', NULL, 'CTRY_CD', 'UK', 'BR_CD', '0654', 'ORG_ID', 888, NULL, NULL, NULL, NULL );
请注意,由于 MODIFY 动态查询过程的目的是通用/通用,即能够更新任何表中的任何字段值,因此在 SQL 语句的构造中要使用的修改字段都是文本字段( VARCHAR),这意味着在构建动态 SQL 语句时,该过程将必须确定要更新的表/字段的字段数据类型是什么,并进一步确定潜在的字段数据类型是什么许多要使用的“标准字段”。
因此,我将一个函数“fncGET_FLD_TYP”放在一起,它可以通过查看系统表 USER_TAB_COLUMNS 来确定字段数据类型,传递表名和字段名以获取字段数据类型。
CREATE or REPLACE FUNCTION "fncGET_FLD_TYP"("tblNAME" IN VARCHAR, "fldNAME" IN VARCHAR) RETURN VARCHAR AS "fldDATA_TYPE" VARCHAR(50);
BEGIN
SELECT DATA_TYPE INTO "fldDATA_TYPE" FROM USER_TAB_COLUMNS WHERE TABLE_NAME = "tblNAME" AND COLUMN_NAME = "fldNAME";
RETURN "fldDATA_TYPE";
END;
出于模拟目的,我创建了一个名为 fncMODIFY 的函数,但我想理想情况下这将作为存储过程运行。
CREATE or REPLACE FUNCTION "fncMODIFY"
RETURN VARCHAR DETERMINISTIC IS
sql_stmt VARCHAR2(4000);
fld_typ VARCHAR2(50);
z_row NUMBER :=0;
CURSOR c_modify IS SELECT * FROM "tblMODIFY_TEST" WHERE "valid_dte" >= "data_dte" OR "valid_dte" IS NULL ORDER BY "mdfy_rank";
BEGIN -- iterate through each of the MODIFY record set, constructing an UPDATE SQL statement on the fly and executing it, setting the table_name.field_name to the 'fld_value', (workout perhaps via a udf function the field data type & convert accordingly), based upon the criteria# fields and their values (via a function workout the field data type & convert accordingly)
FOR r_modify IN c_modify LOOP
sql_stmt := 'UPATE "' || r_modify."tbl_name" || '" SET "' || r_modify."fld_name" || '" = ';
FOR Z in (SELECT COLUMN_NAME FROM USER_TAB_COLUMNS WHERE TABLE_NAME = 'tblMODIFY_TEST') LOOP
z_row := z_row + 1;
CASE
WHEN Z.COLUMN_NAME = 'fld_name' THEN
SELECT "fncGET_FLD_TYP"(r_modify."tbl_name", r_modify."fld_name") INTO fld_typ FROM DUAL;
CASE
WHEN fld_typ = 'DATE' THEN
sql_stmt := sql_stmt || 'TO_DATE(<fld_value>, ''DD.MM.YYYY'')';
WHEN fld_typ IN('FLOAT', 'NUMBER') THEN
--sql_stmt := sql_stmt || 'TO_NUMBER(<fld_value>)';
sql_stmt := sql_stmt || '<fld_value>';
ELSE --'VARCHAR2'
sql_stmt := sql_stmt || ' ''<fld_value>''' || fld_typ ;
END CASE;
sql_stmt := REPLACE(sql_stmt, '<fld_value>', r_modify."fld_value");
WHEN SUBSTR(Z.COLUMN_NAME,1,11) = 'criteriafld' THEN -- AND r_modify.COLUMN_NAME IS NOT NULL THEN
IF z_row = 8 THEN
sql_stmt := sql_stmt || ' WHERE <criteriafld#> = ';
ELSIF z_row > 8 THEN
sql_stmt := sql_stmt || ' AND <criteriafld#> = ';
END IF;
SELECT "fncGET_FLD_TYP"(r_modify."tbl_name", Z.COLUMN_NAME) INTO fld_typ FROM DUAL;
CASE
WHEN fld_typ = 'DATE' THEN
sql_stmt := sql_stmt || 'TO_DATE(<criteriavalue#>, ''DD.MM.YYYY'')';
WHEN fld_typ IN ('FLOAT','NUMBER') THEN
--sql_stmt := sql_stmt || 'TO_NUMBER(<criteriavalue#>)';
sql_stmt := sql_stmt || '<criteriavalue#>';
ELSE -- 'VARCHAR2'
sql_stmt := sql_stmt || '<criteriavalue#>';
END CASE;
sql_stmt := sql_stmt || fld_typ;
--sql_stmt := REPLACE(sql_stmt, '<criteriafld#>', r_modify.criteriafldZ);
--sql_stmt := REPLACE(sql_stmt, '<criteriavalue#>', r_modify.criteriavalueZ);
ELSE -- DO NOTHING
sql_stmt := sql_stmt || '';
END CASE;
END LOOP;
RETURN sql_stmt || ';';
-- EXECUTE.IMMEDIATELY UPDATE tbl_name SET fld_name = fld_value WHERE criteriafld1 = criterivalue1 AND criteriafld# = criterivalue# etc etc
-- EXAMPLE MODIFY_TEST DATA
-- UPDATE "tblDATA_TEST" SET "tb_excl_flg" = -1 WHERE "BR_CD" = '0123';
-- UPDATE "tblDATA_TEST" SET "is_hdgd_flg" = -1 WHERE "ORG_GRP_ID" = 654;
-- UPDATE "tblDATA_TEST" SET "is_incl_flg" = -1 WHERE "data_dte" = TO_DATE('31.10.2020','DD.MM.YYYY');
-- UPDATE "tblDATA_TEST" SET "OUT_AMT" = 800.50 WHERE "ORG_ID" = 321;
-- UPDATE "tblDATA_TEST" SET "OUT_AMT" = 500.50 WHERE "ORG_DESCR" = 'CLIENT E';
-- UPDATE "tblDATA_TEST" SET "OUT_AMT" = 800.50 WHERE "CTRY_CD" = 'UK' AND "BR_CD" = '0654' AND "ORG_ID" = 888;
-- EXECUTE.IMMEDIATELY sql_stmt;
END LOOP;
END;
您可以使用以下 SQL 语句查看每个数据表中的内容
SELECT * FROM "tblDATA_TEST";
SELECT * FROM "tblMODIFY_TEST"; -- Content of the tblMODIFY_TEST table
SELECT * FROM "tblMODIFY_TEST" WHERE "valid_dte" >= "data_dte" OR "valid_dte" IS NULL ORDER BY "mdfy_rank"; -- We only want to process modify updates that are still valid, i.e. where the validation date is in the future, or is NULL
并且此 SQL 语句调用函数 fncMODIFY,该函数尚未执行任何操作,因为我仍在努力获取正确的语法,以便能够传递 fld_name/criteriafld# 字段值以便能够构造 WHERE 条件正确。
SELECT "fncMODIFY" FROM DUAL; -- Call the fuction
因此当前输出为:
UPATE "tblDATA_TEST" SET "tb_excl_flg" = -1 WHERE <criteriafld#> = <criteriavalue#> AND <criteriafld#> = <criteriavalue#> AND <criteriafld#> = <criteriavalue#> AND <criteriafld#> = <criteriavalue#> AND <criteriafld#> = <criteriavalue#>;
但希望是获得上面的示例 SQL 语句并实际执行它们。
任何帮助/指导将不胜感激,并在此先感谢您。