甲骨文 - 更改一周的开始日期 - 星期二

Oracle - Change start day of Week - Tuesday

我想在 Oracle 中更改一周的开始日期,我需要根据 daily/hourly 数据计算每周的一些报告。 通过大量 Google 搜索,我只得到 NLS_TERROTORY 选项,我不知道如何设置为 TUESDAY。就算有领地,那日后又如何变化。提前致谢!

我们欧洲人把星期一作为一周的第一天。因此,如果您将 NLS_TERRITORY 设置为欧洲国家(例如英国,或者 - 为什么不 - 克罗地亚),您将得到“1”作为结果。这是一个简短的演示:

SQL> ALTER SESSION SET NLS_TERRITORY = 'UNITED KINGDOM';

Session altered.

SQL> select to_char(sysdate, 'D') european from dual;

E
-
1

SQL>
SQL> ALTER SESSION SET NLS_TERRITORY = 'AMERICA';

Session altered.

SQL> select to_char(sysdate, 'D') american from dual;

A
-
2

SQL> ALTER SESSION SET NLS_TERRITORY = 'CROATIA';

Session altered.

SQL> select to_char(sysdate, 'D') croatian from dual;

C
-
1

SQL>

[编辑] 自定义 函数 returns 星期几:

SQL> create or replace function f_day_1 (par_date in date default sysdate)
  2    return varchar2
  3  is
  4  begin
  5    return case to_char(par_date, 'FmDay', 'nls_date_language = english')
  6             when 'Monday'    then 7
  7             when 'Tuesday'   then 1
  8             when 'Wednesday' then 2
  9             when 'Thursday'  then 3
 10             when 'Friday'    then 4
 11             when 'Saturday'  then 5
 12             when 'Sunday'    then 6
 13           end;
 14  end;
 15  /

Function created.

SQL> -- today
SQL> select f_day_1 from dual;

F_DAY_1
------------------------------------------------------------------------------
7

SQL> -- tomorrow is Tuesday
SQL> select f_day_1 (date '2018-01-16') from dual;

F_DAY_1(DATE'2018-01-16')
------------------------------------------------------------------------------
1

SQL>

使用此 PL/SQL 块,您可以获得一周中所有可能的开始日期:

BEGIN
    FOR aLang IN (SELECT * FROM V$NLS_VALID_VALUES WHERE parameter = 'TERRITORY' ORDER BY VALUE) LOOP
        EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_TERRITORY = '''||aLang.VALUE||'''';        
        DBMS_OUTPUT.PUT_LINE(aLang.VALUE || ' -> ' || TO_CHAR(TRUNC(SYSDATE, 'D'), 'Day'));
    end loop;   
end;

ALBANIA -> Monday
ALGERIA -> Saturday
AMERICA -> Sunday
ARGENTINA -> Monday
AUSTRALIA -> Monday
AUSTRIA -> Monday
AZERBAIJAN -> Monday
BAHRAIN -> Saturday
BANGLADESH -> Friday
BELARUS -> Monday
BELGIUM -> Monday
BRAZIL -> Sunday
BULGARIA -> Monday
CANADA -> Sunday
CATALONIA -> Monday
CHILE -> Monday
CHINA -> Sunday
CIS -> Monday
COLOMBIA -> Sunday
COSTA RICA -> Sunday
CROATIA -> Monday
CYPRUS -> Monday
CZECH REPUBLIC -> Monday
CZECHOSLOVAKIA -> Monday
DENMARK -> Monday
DJIBOUTI -> Saturday
ECUADOR -> Monday
EGYPT -> Saturday
EL SALVADOR -> Sunday
ESTONIA -> Monday
FINLAND -> Monday
FRANCE -> Monday
FYR MACEDONIA -> Monday
GERMANY -> Monday
GREECE -> Monday
GUATEMALA -> Sunday
HONG KONG -> Sunday
HUNGARY -> Monday
ICELAND -> Monday
INDIA -> Sunday
INDONESIA -> Monday
IRAQ -> Saturday
IRELAND -> Monday
ISRAEL -> Sunday
ITALY -> Monday
JAPAN -> Sunday
JORDAN -> Saturday
KAZAKHSTAN -> Monday
KOREA -> Sunday
KUWAIT -> Saturday
LATVIA -> Monday
LEBANON -> Saturday
LIBYA -> Saturday
LITHUANIA -> Monday
LUXEMBOURG -> Monday
MACEDONIA -> Monday
MALAYSIA -> Sunday
MAURITANIA -> Saturday
MEXICO -> Monday
MOROCCO -> Saturday
NEW ZEALAND -> Monday
NICARAGUA -> Monday
NORWAY -> Monday
OMAN -> Saturday
PANAMA -> Sunday
PERU -> Sunday
PHILIPPINES -> Sunday
POLAND -> Monday
PORTUGAL -> Sunday
PUERTO RICO -> Sunday
QATAR -> Saturday
ROMANIA -> Monday
RUSSIA -> Monday
SAUDI ARABIA -> Saturday
SERBIA AND MONTENEGRO -> Monday
SINGAPORE -> Sunday
SLOVAKIA -> Monday
SLOVENIA -> Monday
SOMALIA -> Saturday
SOUTH AFRICA -> Sunday
SPAIN -> Monday
SUDAN -> Saturday
SWEDEN -> Monday
SWITZERLAND -> Monday
SYRIA -> Saturday
TAIWAN -> Sunday
THAILAND -> Sunday
THE NETHERLANDS -> Monday
TUNISIA -> Saturday
TURKEY -> Monday
UKRAINE -> Monday
UNITED ARAB EMIRATES -> Saturday
UNITED KINGDOM -> Monday
UZBEKISTAN -> Monday
VENEZUELA -> Sunday
VIETNAM -> Sunday
YEMEN -> Saturday
YUGOSLAVIA -> Monday

你看,没有一周的第一天是星期二的地区,所以你必须建立自己的功能。 Littlefoot 的解决方案应该可以正常工作。

您可以创建一个包来包含与新的一周开始相关的自定义函数函数:

注意:一年中的周数是基于包含一周的第一个开始日的一年中的第一周(即星期二 - 所以如果 1 月 1 日是星期三,那么第一周年度将于 1 月 7 日开始)。如果您想要不同的逻辑,则需要修改 WEEK_OF_YEAR 函数。

SQL Fiddle

Oracle 11g R2 架构设置:

CREATE PACKAGE change_week_start IS
  week_start CONSTANT VARCHAR2(9) := 'TUESDAY';

  FUNCTION TRUNC_TO_WEEK_START(
    in_date IN DATE
  ) RETURN DATE;

  FUNCTION DAY_OF_WEEK(
    in_date IN DATE
  ) RETURN NUMBER;

  FUNCTION WEEK_OF_YEAR(
    in_date IN DATE
  ) RETURN NUMBER;

  FUNCTION TO_CHAR_WEEK(
    in_date IN DATE
  ) RETURN VARCHAR2;
END;
/

CREATE PACKAGE BODY change_week_start IS
  FUNCTION TRUNC_TO_WEEK_START(
    in_date IN DATE
  ) RETURN DATE
  IS
  BEGIN
    RETURN NEXT_DAY( TRUNC( in_date ) - 7, week_start );
  END;

  FUNCTION DAY_OF_WEEK(
    in_date IN DATE
  ) RETURN NUMBER
  IS
  BEGIN
    RETURN ( TRUNC( in_date ) - TRUNC_TO_WEEK_START( in_date ) ) + 1;
  END;

  FUNCTION WEEK_OF_YEAR(
    in_date IN DATE
  ) RETURN NUMBER
  IS
  BEGIN
    RETURN TRUNC(
             (
               in_date
               -
               TRUNC_TO_WEEK_START(
                 TRUNC( TRUNC_TO_WEEK_START( in_date ), 'YYYY' ) + 6
               )
             ) / 7
           ) + 1;
  END;

  FUNCTION TO_CHAR_WEEK(
    in_date IN DATE
  ) RETURN VARCHAR2
  IS
  BEGIN
    RETURN TO_CHAR( TRUNC_TO_WEEK_START( in_date ), 'FMYYYY' )
           || '-W' || TO_CHAR( WEEK_OF_YEAR( in_date ), 'FM00' )
           || '-' || DAY_OF_WEEK( in_date );
  END;
END;
/

查询 1:

SELECT value,
       CHANGE_WEEK_START.TO_CHAR_WEEK( value ) AS week,
       TO_CHAR( value, 'DAY' ) AS day
FROM   (
  SELECT TRUNC( SYSDATE, 'YYYY' ) + LEVEL - 1 AS value
  FROM   DUAL
  CONNECT BY LEVEL <= 14
)

Results:

|                VALUE |       WEEK |       DAY |
|----------------------|------------|-----------|
| 2018-01-01T00:00:00Z | 2017-W52-7 | MONDAY    |
| 2018-01-02T00:00:00Z | 2018-W01-1 | TUESDAY   |
| 2018-01-03T00:00:00Z | 2018-W01-2 | WEDNESDAY |
| 2018-01-04T00:00:00Z | 2018-W01-3 | THURSDAY  |
| 2018-01-05T00:00:00Z | 2018-W01-4 | FRIDAY    |
| 2018-01-06T00:00:00Z | 2018-W01-5 | SATURDAY  |
| 2018-01-07T00:00:00Z | 2018-W01-6 | SUNDAY    |
| 2018-01-08T00:00:00Z | 2018-W01-7 | MONDAY    |
| 2018-01-09T00:00:00Z | 2018-W02-1 | TUESDAY   |
| 2018-01-10T00:00:00Z | 2018-W02-2 | WEDNESDAY |
| 2018-01-11T00:00:00Z | 2018-W02-3 | THURSDAY  |
| 2018-01-12T00:00:00Z | 2018-W02-4 | FRIDAY    |
| 2018-01-13T00:00:00Z | 2018-W02-5 | SATURDAY  |
| 2018-01-14T00:00:00Z | 2018-W02-6 | SUNDAY    |

这是我在 2015 年使用 3 个视图自行得出的解决方案,可以动态配置为 运行 任何一年。

--计算一整年的所有天数

CREATE OR REPLACE VIEW  CUSTOM_DAYS_AND_WEEKS
AS
    SELECT DAYS.*,
        CASE
            WHEN ((EXTRACT(YEAR FROM DAYS.WEEK_START)   <>EXTRACT(YEAR FROM DAYS.WEEK_END))
            AND (((TRUNC(WEEK_END,'YYYY')-1)-WEEK_START)>=3))
            THEN TO_CHAR(WEEK_START,'YYYY')
            WHEN ((EXTRACT(YEAR FROM DAYS.WEEK_START) <>EXTRACT(YEAR FROM DAYS.WEEK_END))
            AND (WEEK_START-(TRUNC(WEEK_START,'YYYY'))>=3))
            THEN TO_CHAR(WEEK_END,'YYYY')
            ELSE TO_CHAR(WEEK_START,'YYYY')
        END AS YEAR_NO
    FROM
        (SELECT "A1"."DD" "DD",
            TO_CHAR("A1"."DD",'DY') "DAY",
            DECODE(TO_CHAR("A1"."DD",'DY'),'TUE', "A1"."DD",NEXT_DAY("A1"."DD" -7, 'TUESDAY')) WEEK_START,
            DECODE(TO_CHAR("A1"."DD",'DY'),'MON',"A1"."DD",NEXT_DAY("A1"."DD" , 'MONDAY')) WEEK_END
        FROM
            (SELECT DD+(level-1) "DD"
            FROM
                (SELECT TO_DATE('01012015','DDMMYYYY') "DD" FROM "SYS"."DUAL" "A2"
                )
                CONNECT BY (DD+(level-1))< TO_DATE('01012016','DDMMYYYY')
            ) "A1"
        ) DAYS ;

--对当年和下一年的所有周进行分组(如果年末有的话)

CREATE OR REPLACE VIEW  CUSTOM_WEEKS AS 
    SELECT T.YEAR_NO,
         T.WEEK_START,
         T.WEEK_END,
         SUM(1) S
    FROM  CUSTOM_DAYS_AND_WEEKS T WHERE YEAR_NO>=2015 
    group by T.YEAR_NO, T.WEEK_START, T.WEEK_END
    ORDER BY 1, 2;

--周开始日期、结束日期和周数

CREATE OR REPLACE VIEW  CUSTOM_WEEKS_NO AS    
SELECT  T.YEAR_NO,
         T.WEEK_START,
         T.WEEK_END,
         DECODE (MOD(ROWNUM,(SELECT COUNT(*)+1 FROM  CUSTOM_WEEKS T WHERE T.YEAR_NO=2015)),0,1,MOD(ROWNUM,(SELECT COUNT(*)+1 FROM  CUSTOM_WEEKS T WHERE T.YEAR_NO=2015))) WEEK_NO
FROM  CUSTOM_WEEKS T;

-- 查看创建的Views

SELECT * FROM  CUSTOM_DAYS_AND_WEEKS;    
SELECT * FROM  CUSTOM_WEEKS;

--以周开始日期、结束日期和周数为一年中的所有日期

SELECT D_W.*, W.WEEK_NO 
FROM  CUSTOM_DAYS_AND_WEEKS D_W,
 CUSTOM_WEEKS_NO W
WHERE D_W.WEEK_START=W.WEEK_START
AND D_W.WEEK_END=W.WEEK_END ORDER BY DD DESC;