简体   繁体   中英

ORACLE 12c Unique constraint failing in MERGE command which is matching all columns from the index

I have a table of aggregated data which were collected incorrectly and I am trying to merge results of select into it and I am hitting constraint violation and probably missing something obvious. Table is simple:

 CREATE TABLE "DVRA_STATS_AGG_HOURLY" 
       (    "ID" NUMBER(*,0), 
        "SHIPMENTS" NUMBER, 
        "EVENT_DATETIME" DATE, 
        "COUNTRY" VARCHAR2(2 CHAR), 
        "DATA_TYPE" VARCHAR2(3 CHAR), 
        "EVENT_TYPE" CHAR(15)
       )
    CREATE UNIQUE INDEX "DVRA_STATS_AGG_HOURLY_PK" ON "DVRA_STATS_AGG_HOURLY" ("ID")
    CREATE UNIQUE INDEX "DVRA_STATS_AGG_HOURLY_UK1" ON "DVRA_STATS_AGG_HOURLY" ("EVENT_DATETIME", "COUNTRY", "DATA_TYPE", "EVENT_TYPE")
 CREATE INDEX "DVRA_STATS_AGG_HOURLY_INDEX1" ON "DVRA_STATS_AGG_HOURLY" ("EVENT_DATETIME" DESC)

With trigger sequence and trigger

CREATE OR REPLACE EDITIONABLE TRIGGER "DVRA_STATS_AGG_HOURLY_AINC" 
BEFORE INSERT ON dvra_stats_agg_hourly 
FOR EACH ROW

BEGIN
  SELECT stats_seq.NEXTVAL
  INTO   :new.id
  FROM   dual;
END;

Merge is not complicated either:

MERGE INTO dvra_stats_agg_hourly stats
USING(
    SELECT COUNT(*) as SHIPMENTS, TRUNC(event_datetime,'HH24') as event_datetime,COUNTRY,data_type,event_type
FROM AUDIT 
WHERE   event_type in (<List of events>)
    and TRUNC(event_datetime,'HH24') < trunc(sysdate,'HH24')
    and audittable.event_datetime is not null
    and audittable.COUNTRY is not null
    and audittable.data_type is not null
    and audittable.event_type is not null
GROUP BY TRUNC(event_datetime,'HH24'),COUNTRY,data_type,event_type
    ) audittable 
    ON (
            audittable.event_datetime = stats.event_datetime 
        and audittable.COUNTRY = stats.COUNTRY
        and audittable.data_type = stats.data_type 
        and audittable.event_type = stats.event_type
        )
WHEN MATCHED THEN 
    UPDATE SET stats.SHIPMENTS = audittable.SHIPMENTS WHERE stats.SHIPMENTS <> audittable.SHIPMENTS
WHEN NOT MATCHED THEN 
    INSERT (SHIPMENTS,event_datetime,STATS.COUNTRY,data_type,event_type)
    VALUES (audittable.SHIPMENTS,audittable.event_datetime,audittable.COUNTRY ,audittable.data_type ,audittable.event_type)
;

And I am getting error: ORA-00001: unique constraint (DVRA_MONITORING.DVRA_STATS_AGG_HOURLY_UK1) violated

I am matching on same columns as I am using in unique constraint. I am inserting only not matching rows and I am grouping by same columns so there should be only one row for unique combination of those columns.

I am only one updating table when I am doing this operation, so no other session manipulating data during run of the query.

What am I missing here?

Edit: Data in DVRA_STATS_AGG_HOURLY were inserted with insert into select. With same select which is used in this merge. Just some data were not properly loaded yet, so I am making correction.

Edit2: added not null

Edit3: Changes in merge query after discussion with Alex

MERGE INTO dvra_stats_agg_hourly stats
USING(
    SELECT COUNT(*) as SHIPMENTS, TRUNC(event_datetime,'HH24') as event_datetime,COUNTRY,data_type,event_type
    FROM BCTCUSTOM.V_DVRA_AUDIT 
    WHERE   event_type in (<List of types>)
        and TRUNC(event_datetime,'HH24') < trunc(sysdate,'HH24')
        and TRUNC(event_datetime,'HH24') is not null
        and event_datetime is not null
        and COUNTRY is not null
        and data_type is not null
        and event_type is not null
    GROUP BY TRUNC(event_datetime,'HH24'),COUNTRY,data_type,event_type
    ) audittable 
    ON (
            audittable.event_datetime = stats.event_datetime 
        and ((audittable.COUNTRY is null and stats.COUNTRY is null) or audittable.COUNTRY = stats.COUNTRY)
        and ((audittable.data_type is null and stats.data_type is null) or audittable.data_type = stats.data_type)
        and ((audittable.event_type is null and stats.event_type is null) or audittable.event_type = stats.event_type)
        )
WHEN MATCHED THEN 
    UPDATE SET stats.SHIPMENTS = audittable.SHIPMENTS WHERE stats.SHIPMENTS <> audittable.SHIPMENTS
WHEN NOT MATCHED THEN 
    INSERT (SHIPMENTS,event_datetime,STATS.COUNTRY,data_type,event_type)
    VALUES (audittable.SHIPMENTS,audittable.event_datetime,audittable.COUNTRY ,audittable.data_type ,audittable.event_type)
;

If any of the four columns you are comparing in your ON clause are null then they won't match with your current conditions, since null = null is unknown.

You can add explicit null checks; instead of:

ON (
        audittable.event_datetime = stats.event_datetime 
    and audittable.COUNTRY = stats.COUNTRY
    and audittable.data_type = stats.data_type 
    and audittable.event_type = stats.event_type
    )

do something like:

ON (
        audittable.event_datetime = stats.event_datetime 
    and ((audittable.COUNTRY is null and stats.COUNTRY is null)
       or audittable.COUNTRY = stats.COUNTRY)
    and ((audittable.data_type is null and stats.data_type is null)
       or audittable.data_type = stats.data_type)
    and ((audittable.event_type is null and stats.event_type is null)
       or audittable.event_type = stats.event_type)
    )

db<>fiddle without nulls - works OK.

db<>fiddle with nulls - initially fails, but works with explicit null checks added.


Another possibility is a data type mismatch, as you're using CHAR(15) in the table you showed. If the audit table has that column defined as VARCHAR2(15) instead then the comparison will also fail (because "Oracle uses nonpadded comparison semantics whenever one or both values in the comparison have the data type VARCHAR2 or NVARCHAR2" ), and the implicit conversion during the insert will cause the constraint violation. In that case, you can trim the CHAR value:

ON (
        audittable.event_datetime = stats.event_datetime 
    and audittable.COUNTRY = stats.COUNTRY
    and audittable.data_type = stats.data_type 
    and audittable.event_type = trim(stats.event_type)
    )

db<>fiddle

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM