5

给定一周中某一天的字符串表示形式(即MondayMardiالثلاثاء等)和 NLS_DATE_LANGUAGE 是否有任何方法可以验证一周中的这一天是否有效?

你问为什么这么难?好吧,如果这只是英语,那么显而易见的事情就是:

if <day_variable> in ('Monday','Tuesday', ...) then
   ...

我正在尝试在多个国家/地区扩展地执行此操作,并且我不知道(也不会打扰)为所有当前和未来的 NLS_DATE_LANGUAGE 写出一周中的所有日子,这不是一个真正的选择.

下一个选项是明确的TO_DATE(),它与Friday配合得很好——我怀疑这是一个巧合,因为现在是 5 月,但在本周的剩余时间里却惨遭失败:

SQL> select to_date('Friday', 'Day') as d from dual;

D
----------
2015-05-01

SQL> select to_date('Monday', 'Day') as d from dual;
select to_date('Monday', 'Day') as d from dual
               *
ERROR at line 1:
ORA-01835: day of week conflicts with Julian date

这是一个有趣的错误...... Oracle 建议:

从日期规范中删除星期几值或为儒略日期输入正确的星期几。

删除星期几并不是一个真正的选择,因为这就是我感兴趣的全部,而且我的 Julian 日期没有不正确的星期几,因为我没有 Julian 日期。

其他地方建议的解决方法确认并使用替代日期格式(Dy, dy,FMDy等)相当明显地导致相同的错误。

似乎Oracle内部将所有日期都表示为朱利安日期,并且在这种情况下感到困惑。

如何验证 Oracle 中的命名日期是否正确?

4

3 回答 3

2

据我所知,每次我们修补数据库(当然不会记得这样做)以生成所有可能的日子时,我都会坚持运行类似以下的内容

select value as language
     , to_char(sysdate + l, 'FMDay', 'nls_date_language=''' || value || '') as name_of_day
  from v$nls_valid_values
 cross join ( select level as l from dual connect by level <= 7 )
 where parameter = 'LANGUAGE'
   and isdeprecated = 'FALSE'
 order by language

这样做根本无法扩展,就好像我需要星期几(即 1、2、3 等)一样。该值基于当前的 NLS_TERRITORY,这意味着没有可能的一致性。

to_char(sysdate + l, 'D', 'nls_date_language=''' || value || '') as day_of_week

然后我必须创建一个非确定性的 :-( 函数来验证字符串是否(不)正确:

create or replace function is_day_of_week (
    PDay in varchar2
  , PDate_Language
    ) return number is

   l_ct number;

begin

   select count(*) into l_ct
     from days_of_the_week
    where name_of_day = PDay
      and language = PDate_Language;

   return l_ct;

end is_day_of_week;
于 2015-05-05T08:33:20.987 回答
2

请注意,包含星期几的任何 7 个连续日历日期的字符串表示必须将测试的星期几字符串作为子字符串,前提是测试的字符串有效。

因此,为了规避错误,请连接以 sysdate 开头的一周的字符串表示形式,并根据结果测试您的字符串:

SELECT CASE INSTR(
               to_char ( sysdate + 0, 'Day, DD.MM.YYYY' )   
            || to_char ( sysdate + 1, 'Day, DD.MM.YYYY' )
            || to_char ( sysdate + 2, 'Day, DD.MM.YYYY' )
            || to_char ( sysdate + 3, 'Day, DD.MM.YYYY' )
            || to_char ( sysdate + 4, 'Day, DD.MM.YYYY' )
            || to_char ( sysdate + 5, 'Day, DD.MM.YYYY' )
            || to_char ( sysdate + 6, 'Day, DD.MM.YYYY' )
          , '<the_string_to_test>' )
         WHEN 0 THEN 'invalid'
         ELSE        'valid'
       END  isvalid
  FROM DUAL
     ;

作为一种改进,如果格式字符串和测试字符串可以在测试字符串是有效日期名称的子字符串的情况下防止误报,则围绕日期部分的分隔符。

SELECT CASE INSTR(
               to_char ( sysdate + 0, '.FMDay., DD.MM.YYYY' )   
            || to_char ( sysdate + 1, '.FMDay., DD.MM.YYYY' )
            || to_char ( sysdate + 2, '.FMDay., DD.MM.YYYY' )
            || to_char ( sysdate + 3, '.FMDay., DD.MM.YYYY' )
            || to_char ( sysdate + 4, '.FMDay., DD.MM.YYYY' )
            || to_char ( sysdate + 5, '.FMDay., DD.MM.YYYY' )
            || to_char ( sysdate + 6, '.FMDay., DD.MM.YYYY' )
          , '.' || '<the_string_to_test>' || '.')
         WHEN 0 THEN 'invalid'
         ELSE        'valid'
       END  isvalid
  FROM DUAL
     ;

编辑/注释

在后一个示例中,请注意...

  • 并非所有分隔符都有效(使用.[works] 和|[fails] 测试)
  • FMDay必须用作格式说明符以从星期几名称中修剪尾随空格

编辑#2

这是相同的(几乎)不需要字符串操作:

select 
  case when upper(:day_name) in 
  (
    select to_char(sysdate + 0, 'FMDAY', 'NLS_DATE_LANGUAGE=''' || :nls_date_lang || '''') from dual union all
    select to_char(sysdate + 1, 'FMDAY', 'NLS_DATE_LANGUAGE=''' || :nls_date_lang || '''') from dual union all
    select to_char(sysdate + 2, 'FMDAY', 'NLS_DATE_LANGUAGE=''' || :nls_date_lang || '''') from dual union all
    select to_char(sysdate + 3, 'FMDAY', 'NLS_DATE_LANGUAGE=''' || :nls_date_lang || '''') from dual union all
    select to_char(sysdate + 4, 'FMDAY', 'NLS_DATE_LANGUAGE=''' || :nls_date_lang || '''') from dual union all
    select to_char(sysdate + 5, 'FMDAY', 'NLS_DATE_LANGUAGE=''' || :nls_date_lang || '''') from dual union all
    select to_char(sysdate + 6, 'FMDAY', 'NLS_DATE_LANGUAGE=''' || :nls_date_lang || '''') from dual
  ) then 'valid' else 'invalid'
  end  isvalid
from dual;
于 2015-05-05T09:00:13.627 回答
0

简短的回答是:你不能用 Oracle 做到这一点。

可以使用带有 NEXT DAY 的循环编写如下 PL/SQL 函数:

create or replace function is_valid_dayname(
  vi_dayname varchar2, 
  vi_nls_date_language varchar2) return integer 
as
  -- Oracle day name constants for NEXT_DAY
  type type_daynames is varray(7) of varchar2(100);
  v_daynames type_daynames := type_daynames('SUNDAY','MONDAY','TUESDAY','WEDNESDAY','THURSDAY','FRIDAY','SATURDAY');
  -- date variable for the INTO clause
  v_date date;
begin
  for i in 1 .. 7 loop
    begin 
      select to_date(vi_dayname || to_char(next_day(sysdate, v_daynames(i)), 'yyyymmdd', 'NLS_DATE_LANGUAGE=''' || vi_nls_date_language || ''''), 'day yyyymmdd') into v_date from dual;
      return 1; -- success
    exception when others then null;
    end;
  end loop;
  return 0; -- failure
end;

并这样使用它:

select is_valid_dayname('invalid dayname', 'GERMAN') from dual;

0

select is_valid_dayname('samstag', 'GERMAN') from dual;

1

select is_valid_dayname('SAMSTAG', 'GERMAN') from dual;

1

select is_valid_dayname('Samstag', 'GERMAN') from dual;

1

select is_valid_dayname('Sonnabend', 'GERMAN') from dual;

0

糟糕,“Sonnabend”是德语中“Samstag”的同义词,但 Oracle 不知道这一点。所以我们接近了一个解决方案,但它并不完整。

于 2015-05-05T09:47:21.277 回答