#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?