Oracle
 sql >> Base de données >  >> RDS >> Oracle

ON CONVERSION ERROR échoue avec ORA-43918 :cet argument doit être un littéral

CURSOR_SHARING

L'ON CONVERSION ERROR La fonctionnalité ne fonctionne pas lorsque le paramètre CURSOR_SHARING est défini sur FORCE. Pour éviter cette erreur, modifiez le paramètre au niveau du système, de la session ou de l'instruction.

Idéalement, CURSOR_SHARING doit être défini sur EXACT pour l'ensemble du système. Mais si nous avons une application qui n'utilise pas de variables de liaison, nous ne pouvons probablement pas exécuter alter system set cursor_sharing=exact; .

Le paramètre peut être défini au niveau de la session avec alter session set cursor_sharing=exact; , mais il n'est pas toujours pratique de changer constamment les paramètres de session.

Le paramètre peut être modifié au niveau de l'instruction avec l'indice CURSOR_SHARING_EXACT :

SQL> select /*+ cursor_sharing_exact */ to_date(the_date default null on conversion error, 'MM/DD/YYYY') the_date
  2  from
  3  (
  4      select '1/1/2021' the_date from dual union all
  5      select 'bad date' the_date from dual
  6  );

THE_DATE
---------
01-JAN-21

Bogue de l'analyseur/optimiseur

Comme @gouessej l'a découvert, il existe une autre raison potentielle de l'erreur ORA-43918 qui n'est pas liée au partage de curseur. Il semble y avoir des bogues d'analyse ou d'optimisation liés à la transformation de CASE et TO_ fonctionne sur certaines versions d'Oracle.

Par exemple, l'instruction SQL ci-dessous échoue sur Oracle 18c et 19c :

SQL> select case when v_num is null then 0 else v_num end
  2  from
  3  (
  4      select to_number('120.3' default null on conversion error, '99999D99') as v_num
  5      from dual
  6  );
    select to_number('120.3' default null on conversion error, '99999D99') as v_num
                                                               *
ERROR at line 4:
ORA-43918: This argument must be a literal

Je pense qu'il s'agit d'un bogue d'analyse ou d'optimisation car l'erreur disparaît si vous arrêtez les transformations en ajoutant un prédicat comme rownum >= 1 . (Quand Oracle voit ROWNUM , il suppose que les résultats doivent être affichés dans un certain ordre et n'appliquera pas autant de transformations à ce bloc de requête.)

SQL> select case when v_num is null then 0 else v_num end
  2  from
  3  (
  4      select to_number('120.3' default null on conversion error, '99999D99') as v_num
  5      from dual
  6  where rownum >= 1
  7  );

CASEWHENV_NUMISNULLTHEN0ELSEV_NUMEND
------------------------------------
                               120.3