A po co robisz TO_CHAR na dacie? INVOICE_DATE jest typu VARCHAR2? Tak samo inne daty, jeśli mają format DATE czy TIMESTAMP, to porównujesz je bezpośrednio z datą a nie znakami. Bo wtedy robi się niejawna konwersja.
contract_end_date < sysdate
a lepiej zapewne (bo raczej contract_end_date ma godzinę = 0000):
contract_end_date < trunc(sysdate)
Zamiast:
to_char(sysdate, 'YYYYMM')|| '01'
robisz obcięcie daty do pierwszego dnia miesiąca:
trunc(sysdate, 'MM')
Potem
to_char(add_months(sysdate,- 6), 'YYYYMM')|| '01'
zmień na
trunc(add_months(trunc(sysdate),- 6), 'MM')
Generalna zasada, jeśli masz dane bez godzin (tj. równe 0000), to porównuj też z parametrami bez godzin. Jeśli zawierają godziny, porównuj z jakąś stałą godziną np. 2359, albo 0000 dnia następnego, aby uniknąć "pływających danych" tj. różnych wyników zależnie od godziny wykonania zapytania (no chyba, że działasz na danych typowo związanych z czasem i chcesz widzieć dokładnie te utworzone przed bieżącą godziną).
Kolejna rzecz, CUST_ID wygląda na liczbę, po co konwersja na znaki?
CUST_ID IN (2139906, 2140120, 2140174, 2139800)
Zawsze sprawdzaj typ danych w definicji tabeli i używaj funkcji dostosowanych do tego typu. Jeśli robisz niejawne konwersje jak powyżej, to nie są one odporne na zmianę ustawień regionalnych, dotyczy to dat oraz liczb.
Ty sobie zrobisz konwersję daty do YYYYMMDD, a sesja będzie miała ustawione DDMMYYYY, więc baza porówna np. '31122023' z '20231231'
w warunku:
old.contract_end_date < to_char(sysdate, 'YYYYMMDD')