Using the comments made in the conversation - appreciate all the suggestions - I have a PL/SQL iterative loop followed by the merge concept that was suggested earlier.
Ultimately, in the examples you see a table called header that has 4 rows of data. These are sample data made up so nothing needs to be obfuscated.
Each record is analyzed for certain columns in the table to determine based off logic in the LOOP below whether that column or any combination of column varying by each records needs to get updated. In some cases a record may not need to be updated, some cases where just 1 column, or cases more than one column needs to be updated.
The requirement though is that a source header record cannot just be updated. To satisfy the requirement if a record has a dollar amount of $50 for example - then a record has to be inserted to show a -$50 (only difference in this record than the source is the scenario) and then another record of $50 created showing where the money is moving to with the appropriate columns that required an update updated plus the scenario column.
The code (the loop) parses through the records and determines if any record is eligible for an update on a column, if it identifies at least one update then the two adjustment records need to be loaded into a mapping table to be used in the merge statements (someone here suggested this and was very helpful) to merge int the main header table. If the loop sees no updates are required based off the record values then no adjust records are written to the mapping table.
The code parses each important column (not scenario and the amount columns as those are never drivers) - you see the logic that if the value doesnt require an update then the new value equals the old value. If a column requires an update then the newvalue equals the new value. The counter notes whether for each parsed record whether there is at least one adjustment registered. If so - then that entire record is unique and needs to be written to the mapping table even if the majority of the column values didn't change.
The example below is very much a sample - the real use case will require many fields and will require many more variables and more updated logic for each field.
Note I use the %type - I would personally like to use the %rowtype to do the same thing inside the loop below. There will be cases where I would want to analyze the entire record in a more efficient way.
Is there a way I can accomplish the same logic (perhaps with a formal cursor or select into a variable) to accomplish the same thing?
Example Header Records
| ORG_CODE | PROJECT_CODE | OUTPUT_CODE | VERSION | AMOUNT_DOLLARS |
|---|---|---|---|---|
| E4JAZ | P04 | O234 | Actual | 50 |
| D_ORG | P01 | D_OUTPUT | Actual | 70 |
| E4JAZ | P_PROJ | D_OUTPUT | Actual | 50 |
After the below loop code is executed - adjust_rows_mapping records (not the result post-merge but just the adjust_rows_mapping) - Note the header before the fields - aligned by comma
Note that despite the Header table having 3 records - only two of the 3 records (records 1 & 3) have at least one adjustment on a column registered. Record 1 requires 3 adjustments to all 3 fields (shown in the code logic) and record 3 requires just 1 (just to the org) also shown in the logic. Record 2 has no columns that per the logic does not have values that need adjustment. Therefore, the mapping table only gets two records inserted. The non-changing columns in record 3 stay the same despite the updated 1st column in the adjustment record.
| OLD_ORG_CODE | OLD_PROJECT_CODE | NEW_ORG_CODE | NEW_PROJECT_CODE | OLD_OUTPUT_CODE | NEW_OUTPUT_CODE |
|---|---|---|---|---|---|
| E4JAZ | P04 | F4JAZ | P01 | O234 | test_output |
| E4JAZ | P_PROJ | F4JAZ | P_PROJ | D_OUTPUT | D_OUTPUT |
See code below - ultimately in the loop I am hoping to find a cleaner way using %ROWTYPE to accomplish this and also provide the ability to scale this up as there will be inner joins to mapping tables embedded in the cursor or perhaps sub-loops.
Any ideas would be greatly appreciated!
Thanks All!
DECLARE
v_old_org_code header.org_code%TYPE;
v_old_proj_code header.project_code%TYPE;
v_new_org_code header.org_code%TYPE;
v_new_proj_code header.project_code%TYPE;
v_old_output_code header.output_code%TYPE;
v_new_output_code header.output_code%TYPE;
v_old_version_code header.VERSION%TYPE;
v_new_version_code header.VERSION%TYPE;
v_new_amount_dollars header.AMOUNT_DOLLARS%TYPE;
v_old_amount_dollars header.AMOUNT_DOLLARS%TYPE;
v_counter NUMBER;
begin
FOR record IN (SELECT ORG_CODE, PROJECT_CODE, OUTPUT_CODE, VERSION, AMOUNT_DOLLARS FROM HEADER) LOOP
v_counter := 0;
v_old_org_code := record.ORG_CODE;
v_old_proj_code := record.PROJECT_CODE;
v_old_output_code := record.OUTPUT_CODE;
v_old_version_code := record.VERSION;
v_old_amount_dollars := record.AMOUNT_DOLLARS;
v_new_org_code := record.ORG_CODE;
v_new_proj_code := record.PROJECT_CODE;
v_new_output_code := record.OUTPUT_CODE;
v_new_version_code := record.VERSION;
v_new_amount_dollars := record.AMOUNT_DOLLARS;
IF v_old_org_code = 'E4JAZ' THEN
v_new_org_code := 'F4JAZ';
v_counter := v_counter + 1;
end if;
if v_old_proj_code in ('P04', 'P07') then v_new_proj_code := 'P01';
v_counter := v_counter + 1;
end if;
if v_old_output_code in ('O234') then v_new_output_code := 'test_output';
v_counter := v_counter + 1;
END IF;
if v_counter > 0
then
insert into adjust_rows_mapping (old_org_code, old_project_code, new_org_code, new_project_code
, old_output_code, new_output_code)
--)
values (v_old_org_code, v_old_proj_code , v_new_org_code, v_new_proj_code
, v_old_output_code, v_new_output_code );
END IF;
END LOOP;
MERGE INTO header dst
USING (
WITH adjust_rows (old_org_code, old_project_code, new_org_code, new_project_code, old_output_code, new_output_code) AS (
SELECT * from adjust_rows_mapping
)
SELECT u.org_code,
u.project_code,
u.output_code,
'Actual_Adjust' AS version,
h.amount_dollars * u.multiplier AS amount_dollars
FROM adjust_rows a
UNPIVOT (
(old_org_code, old_project_code, old_output_code, org_code, project_code, output_code)
FOR multiplier IN (
(old_org_code, old_project_code, old_output_code, old_org_code, old_project_code, old_output_code) AS -1,
(old_org_code, old_project_code, old_output_code, new_org_code, new_project_code, new_output_code) AS 1
)
) u
INNER JOIN header h
ON h.org_code = u.old_org_code
AND h.project_code = u.old_project_code
and h.output_code = u.old_output_code
) src
ON ( src.org_code = dst.org_code
AND src.project_code = dst.project_code
AND src.output_code = dst.output_code
AND src.version = dst.version
AND src.amount_dollars = dst.amount_dollars )
WHEN NOT MATCHED THEN
INSERT (Org_Code, Project_Code, Output_Code, Version, Amount_Dollars)
VALUES (src.Org_Code, src.Project_Code, src.Output_Code, src.Version, src.Amount_Dollars);
END;
/
CREATE TABLEandINSERTstatements for your sample data; an complete explanation of the logic that you want to implement; the expected output for that sample data; and YOUR attempt at a solution.