Сбой параллельного выполнения программы, если она выполняется французским пользователем

#sql #oracle

#sql #Oracle

Вопрос:

Мы разработали одну процедуру, которая извлекает данные для проектирования основных средств и создает выходные данные .csv. Мы зарегистрировали параллельную программу в приложении Oracle. Мы узнали, что когда бизнес-пользователи из США или Великобритании запускают эту программу, она работает нормально, но французский пользователь запускает эту программу, она завершается с ошибкой.

Когда я проверил в файле журнала, я вижу, что при создании функции listagg происходит сбой при выполнении программы.

  select listagg('''' || TO_CHAR(PERIOD_NAME,'MON-YY') || ''' as "' || TO_CHAR(PERIOD_NAME,'MON-YY') || '"', ',') within group (order by PERIOD_NAME)
  into   pivot_clause
  from   (select   TO_DATE(PERIOD_NAME,'MON-YYYY') PERIOD_NAME from FA_PROJ_INTERIM_RPT where request_id = p_req_id
  GROUP BY TO_DATE(PERIOD_NAME,'MON-YYYY') order by TO_DATE(PERIOD_NAME,'MON-YYYY') ASC);
  

Комментарии:

1. Что содержит PERIOD_NAME ? April — это APR для английского, AVR для французского…

2. Добро пожаловать в Stack Overflow. Пожалуйста, отредактируйте свой вопрос с помощью edit кнопки чуть ниже тегов вопросов и включите информацию о возникшей ошибке. Спасибо.

3. Название периода содержит: апрель-19 АВГУСТ-19 декабрь-19 ФЕВРАЛЬ-20 январь-20

4. Название периода @LucM содержит: апрель-19 АВГУСТ-19 декабрь-19 ФЕВРАЛЬ-20 январь-20

5. «это завершится с ошибкой» Какую ошибку вы видите в файле журнала? Пожалуйста, предоставьте нам всю имеющуюся у вас информацию, чтобы нам не пришлось догадываться о проблеме.

Ответ №1:

Во-первых, я согласен с Luc M в том, что было бы лучше изменить вашу модель данных, чтобы использовать формат ISO 8601, и избегать именования месяцев. Но я понимаю, что изменение модели не всегда является вариантом.

Если данные должны храниться на английском языке, но преобразовываться неанглоязычными сеансами, вы можете установить явный параметр NLSPARAM в TO_DATE функции, чтобы гарантировать, что преобразование всегда происходит на английском языке. Если не задано, NLSPARAM будет использовать значение сеанса по умолчанию.

Например, я считаю, что этот тест воспроизводит вашу проблему:

 --Returns: 2019-04-01
alter session set nls_language = 'AMERICAN';
select to_date('APR-2019', 'MON-YYYY') the_date from dual;

--Fails with: ORA-01843: ce n'est pas un mois valide
alter session set nls_language = 'CANADIAN FRENCH';
select to_date('APR-2019', 'MON-YYYY') the_date from dual;
  

Это код для исправления:

 --Returns: 2019-04-01
alter session set nls_language = 'AMERICAN';
select to_date('APR-2019', 'MON-YYYY', 'NLS_DATE_LANGUAGE = American') the_date from dual;

--Returns: 2019-04-01
alter session set nls_language = 'FRENCH';
select to_date('APR-2019', 'MON-YYYY', 'NLS_DATE_LANGUAGE = American') the_date from dual;
  

Интернационализация затруднена. Меня не удивит, если в моем приведенном выше коде есть еще одна ошибка, возможно, для другого календаря.

Ответ №2:

Проблема в том, что вы пытаетесь преобразовать в ДАТУ, которая не является датой на французском языке.

Чтобы заставить это работать, у вас есть 2 варианта.

Удалите TO_CHAR и TO_DATE из вашего запроса.

   select listagg('''' || PERIOD_NAME || ''' as "' || PERIOD_NAME || '"', ',')   
  within group (order by PERIOD_NAME)
  into   pivot_clause
  from   (select PERIOD_NAME as PERIOD_NAME 
          from FA_PROJ_INTERIM_RPT 
          where request_id = p_req_id
          GROUP BY PERIOD_NAME
          order by PERIOD_NAME ASC
        );
  

Переведите аббревиатуру месяца на французский (вы должны ее закодировать …)

 JAN --> JAN  
FEB --> FEV  
MAR --> MAR  
APR --> AVR  
MAY --> MAI  
JUN --> JUN  
JUL --> JUI  
AUG --> AOU  
SEP --> SEP  
OCT --> OCT  
NOV --> NOV  
DEC --> DEC
  

С самого начала PERIOD_NAME должен был быть 2019-01 для января 2019.

Сортировать проще, перевод не требуется.

Редактировать

Подходящее решение для вашей проблемы, как объяснил Джон Хеллер, заключается в том, чтобы изменять на лету значение NLS_LANGUAGE.

Но я бы посоветовал вам сбросить ее на исходный язык после выполнения вашего запроса, чтобы избежать проблем в других местах

 original_language := userenv( 'LANG' );

-- I suppose by French you mean French Canadian...
IF original_language = 'FRC' THEN 
   EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_LANGUAGE=AMERICAN';
END IF;

-- execute your query here

IF original_language = 'FRC' THEN 
   EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_LANGUAGE=CANADIAN FRENCH';
END IF;
  

Код не был протестирован. Я написал несколько строк в качестве примера, потому что я думаю, что это лучше, чем объяснения.

Комментарии:

1. Мы получаем название периода от FA_PROJ_INTERIM_RPT после предоставления идентификатора запроса. Когда я проверил в FA_PROJ_INTERIM_RPT в столбце period_name, я вижу, что периоды указаны в календаре Великобритании, т. е. январь ФЕВРАЛЬ МАРТ АПРЕЛЬ МАЙ ИЮНЬ ИЮЛЬ АВГУСТ СЕНТЯБРЬ ОКТЯБРЬ НОЯБРЬ ДЕКАБРЬ

2. @prashantkulkarni — «Название периода содержит: апрель-19 АВГУСТ-19 декабрь-19 февраль-20 январь-20.» Таким образом, инструкция TO_DATE(PERIOD_NAME,'MON-YYYY') будет проблемой, если ваши французские пользователи работают с NLS_DATE_LANGUAGE=FRENCH set в своих клиентских средах.

3. Спасибо Люку и Джону за ваш совет. После завершения выполнения программы нужно ли мне сбросить NLS_LANGUAGE?