Here are the 3 individual, standardized SQL statements.
These use the "Best Practice" logic we established:
Composite Exclusion: Uses
DATA_OBJECT_ID IS NOT NULL(Much faster/cleaner than joiningALL_TAB_PARTITIONS).Subpartition Support: Automatically includes
TABLE SUBPARTITION.Safety: The Update statement includes the Old Values and Object Type to ensure you never update the wrong row.
1. The "Live Data" Query (Source)
Run this to get the New values from the database.
Note: This replaces your complex UNION ALL query. It automatically filters out logical "Parent" partitions (Composite=YES) by ensuring
DATA_OBJECT_IDexists.
OBJECT_ID,
DATA_OBJECT_ID,
OBJECT_NAME AS TABLE_NAME,
SUBOBJECT_NAME AS PARTITION_NAME, -- Holds Partition or Subpartition Name
OBJECT_TYPE
FROM ALL_OBJECTS
WHERE OWNER = 'YOUR_SCHEMA_NAME' -- <1-- Change Owner
AND OBJECT_NAME = 'YOUR_TABLE_NAME' -- <2-- Change Table Name
AND OBJECT_TYPE IN ('TABLE', 'TABLE PARTITION', 'TABLE SUBPARTITION')
AND DATA_OBJECT_ID IS NOT NULL -- <3-- This ensures COMPOSITE = 'NO'
ORDER BY
OBJECT_NAME,
OBJECT_TYPE,
SUBOBJECT_NAME;
2. The "Captured Data" Query (Target)
Run this to see what you currently have stored (the Old values).
3. The "Fix" Statement (Update)
Use the values from Statement 1 (Live) to update Statement 2 (Target).
Replace the placeholders
<...>with the actual numbers/names.
SET OBJECT_ID = <NEW_OBJECT_ID_FROM_STEP_1>,
DATA_OBJECT_ID = <NEW_DATA_OBJECT_ID_FROM_STEP_1>
WHERE PUR_ID = 279
AND TABLE_NAME = 'YOUR_TABLE_NAME'
-- 1. IDENTIFY THE PARTITION (Handle NULLs for non-partitioned tables)
AND NVL(PARTITION_NAME, '###') = NVL('<PARTITION_NAME_FROM_STEP_1>', '###')
-- 2. LOCK THE TYPE (Ensures you don't mix up Partition vs Subpartition)
AND OBJECT_TYPE = '<OBJECT_TYPE_FROM_STEP_1>'
-- 3. SAFETY CHECK (Only update if it matches the OLD values from Step 2)
AND OBJECT_ID = <OLD_OBJECT_ID_FROM_STEP_2>
AND DATA_OBJECT_ID = <OLD_DATA_OBJECT_ID_FROM_STEP_2>;
=================================================
Master Script:
/*
This is the most seamless approach. We will wrap the logic in a standard PL/SQL block.
How this works:
Run it as is: It acts exactly like your "Health Check" query (Preview Mode). It prints the changes to the
DBMS_OUTPUTwindow.Change one word: Change
v_apply_changestoTRUEand run it again. It executes the Merge.
This guarantees that what you see in the report is exactly what gets applied.
The "All-in-One" Script
/* Here is the updated Robust Health Check script.
I have decoupled the filter logic. Now, the captured_db block is completely independent, allowing you to manually type whatever WHERE clause you need (single ID, multiple IDs, date ranges, etc.).
The script is smart enough to use your manual list to filter the live_db query automatically, keeping performance high.
*/
Part 1: The Preview Report (ReadOnly)
Run this query first. It joins your table with the live database using the COMPOSITE='NO' logic (Physical Segments only) and tells you the status of every object.
This is the "Best of the Best" approach. It strictly follows your logic (validating COMPOSITE='NO') and gives you exactly what you asked for:
Part 1: A clean Report (Preview) showing the comparison side-by-side.
Part 2: A generator that writes the SQL statements for you. It produces the exact
UPDATEscripts with the specificWHEREclauses you requested.
Part 1: The Preview Report (ReadOnly)
Run this query first. It joins your table with the live database using the COMPOSITE='NO' logic (Physical Segments only) and tells you the status of every object.
WITH config AS (
SELECT 279 AS filter_pur_id, 'YOUR_SCHEMA_NAME' AS filter_owner FROM dual
),
live_data AS (
-- Equivalent to your "Composite = NO" query, but faster
SELECT object_name, subobject_name, object_type, object_id, data_object_id
FROM all_objects, config
WHERE owner = config.filter_owner
AND object_type IN ('TABLE', 'TABLE PARTITION', 'TABLE SUBPARTITION')
AND data_object_id IS NOT NULL -- This filters out COMPOSITE partitions
),
captured_data AS (
SELECT table_name, partition_name, object_type, object_id, data_object_id
FROM db_object_id_captured, config
WHERE pur_id = config.filter_pur_id
)
SELECT
NVL(live.object_name, cap.table_name) AS TABLE_NAME,
NVL(live.subobject_name, cap.partition_name) AS PARTITION_NAME,
-- LIVE VALUES
live.object_id AS LIVE_OBJ_ID,
live.data_object_id AS LIVE_DATA_ID,
-- CAPTURED VALUES
cap.object_id AS DB_CAPTURED_OBJ_ID,
cap.data_object_id AS DB_CAPTURED_DATA_ID,
-- STATUS CALCULATION
CASE
WHEN cap.object_id IS NULL THEN 'NEW (Insert Needed)'
WHEN live.object_id IS NULL THEN 'ORPHAN (Delete Needed)'
WHEN live.object_id != cap.object_id OR
NVL(live.data_object_id, -1) != NVL(cap.data_object_id, -1)
THEN 'NEEDS TO BE UPDATED'
ELSE 'THEY ARE IN SYNC'
END AS STATUS
FROM live_data live
FULL OUTER JOIN captured_data cap
ON live.object_name = cap.table_name
AND NVL(live.subobject_name, '###') = NVL(cap.partition_name, '###')
AND live.object_type = cap.object_type
WHERE
-- Filter to show only specific table if needed, or leave commented for all
-- live.object_name = 'YOUR_TABLE_NAME' AND
(live.object_id != cap.object_id OR
NVL(live.data_object_id, -1) != NVL(cap.data_object_id, -1) OR
cap.object_id IS NULL OR live.object_id IS NULL)
ORDER BY 1, 2;
Part 2: The SQL Generator (The "Fix" Script)
This PL/SQL block does not execute changes. Instead, it prints the specific UPDATE statements you asked for to the "Dbms Output" window. You can then copy-paste them and run them manually.
It checks
COMPOSITE='NO'logic.It includes the Old Values in the
WHEREclause as requested.SET SERVEROUTPUT ON SIZE 100000;
DECLARE
v_pur_id NUMBER := 279;
v_schema_owner VARCHAR2(50) := 'YOUR_SCHEMA_NAME';
BEGIN
DBMS_OUTPUT.PUT_LINE('-- GENERATED UPDATE STATEMENTS (INCLUDES SUBPARTITIONS) --');
FOR r IN (
SELECT
live.object_name,
live.subobject_name,
live.object_type,
-- New Values (From Live DB)
live.object_id AS new_obj_id,
live.data_object_id AS new_data_id,
-- Old Values (From Your Table)
cap.object_id AS old_obj_id,
cap.data_object_id AS old_data_id
FROM all_objects live
JOIN db_object_id_captured cap
ON live.object_name = cap.table_name
-- Matches Partition OR Subpartition Name automatically
AND NVL(live.subobject_name, '###') = NVL(cap.partition_name, '###')
AND live.object_type = cap.object_type
WHERE live.owner = v_schema_owner
AND cap.pur_id = v_pur_id
AND live.data_object_id IS NOT NULL -- Composite=NO Logic
AND live.object_type IN ('TABLE', 'TABLE PARTITION', 'TABLE SUBPARTITION')
-- CRITICAL: Only generate updates if IDs drifted
AND (live.object_id != cap.object_id OR
NVL(live.data_object_id, -1) != NVL(cap.data_object_id, -1))
ORDER BY live.object_name, live.subobject_name
) LOOP
-- 1. HEADER
DBMS_OUTPUT.PUT_LINE('UPDATE DB_OBJECT_ID_CAPTURED');
DBMS_OUTPUT.PUT_LINE('SET OBJECT_ID = ' || r.new_obj_id ||
', DATA_OBJECT_ID = ' || r.new_data_id);
-- 2. IDENTIFIERS (The "Where" Clause)
DBMS_OUTPUT.PUT_LINE('WHERE PUR_ID = ' || v_pur_id);
DBMS_OUTPUT.PUT_LINE(' AND TABLE_NAME = ''' || r.object_name || '''');
-- Extended Logic: Explicitly handle Partitions vs Subpartitions in the WHERE clause
IF r.subobject_name IS NOT NULL THEN
DBMS_OUTPUT.PUT_LINE(' AND PARTITION_NAME = ''' || r.subobject_name || '''');
ELSE
DBMS_OUTPUT.PUT_LINE(' AND PARTITION_NAME IS NULL');
END IF;
-- [NEW] Explicitly lock the Object Type (Table vs Partition vs Subpartition)
DBMS_OUTPUT.PUT_LINE(' AND OBJECT_TYPE = ''' || r.object_type || '''');
-- 3. SAFETY CHECK (Old Values)
DBMS_OUTPUT.PUT_LINE(' AND OBJECT_ID = ' || r.old_obj_id);
IF r.old_data_id IS NOT NULL THEN
DBMS_OUTPUT.PUT_LINE(' AND DATA_OBJECT_ID = ' || r.old_data_id || ';');
ELSE
DBMS_OUTPUT.PUT_LINE(' AND DATA_OBJECT_ID IS NULL;');
END IF;
DBMS_OUTPUT.PUT_LINE('------------------------------------------');
END LOOP;
END;
/
No comments:
Post a Comment