Oracle Express Edition(浏览器界面)与 SQL Plus 命令行
Oracle Express Edition (Browser Interface) vs. SQL Plus Commd Line
我在线使用Application Express(安装在Win7 32位上的OracleXE 11g Express Edition),下面的存储过程编译执行成功,没有错误。在 SQL Plus 命令行编译时,代码编译成功,但是当我执行时,它给我一个错误。现在我已经解决了错误(如下所示的代码片段),我的问题是为什么 Application Express 中的数据库引擎不抛出错误?希望我能正确解释我的问题。
Table定义
CREATE TABLE DATE_DIMENSION
(
DATE_KEY DATE NOT NULL,
FULL_DATE_DESCRIPTION VARCHAR2(64) NOT NULL,
DAY_OF_WEEK NUMBER(1,0) NOT NULL,
DAY_OF_MONTH NUMBER(2,0) NOT NULL,
DAY_OF_YEAR NUMBER(3,0) NOT NULL,
LAST_DAY_OF_WEEK_INDICATOR CHAR(1) NOT NULL,
LAST_DAY_OF_MONTH_INDICATOR CHAR(1) NOT NULL,
WEEK_ENDING_DATE DATE NOT NULL,
MONTH_NUMBER NUMBER(2,0) NOT NULL,
MONTH_NAME VARCHAR2(32) NOT NULL,
YEAR_MONTH CHAR(32) NOT NULL,
QUARTER_NUMBER NUMBER(1,0) NOT NULL,
YEAR_QUARTER CHAR(32) NOT NULL,
YEAR_NUMBER NUMBER(4,0) NOT NULL,
CONSTRAINT DATE_DIMENSION_PK PRIMARY KEY (DATE_KEY)
)
/
存储过程
create or replace PROCEDURE sp_DATE_DIMENSION(v_START_YEAR IN INT, v_END_YEAR IN INT) AS
--Declare two variables as DATE datatypes
v_CURRENT_DATE DATE;
v_END_DATE DATE;
BEGIN
--Assign the start year and end year to it's respective variables
v_CURRENT_DATE := TO_DATE('0101' || v_START_YEAR, 'MMDDYYYY');
v_END_DATE := TO_DATE('1231' || v_END_YEAR, 'MMDDYYYY');
--Clear/Dump what is currently stored in the table
DELETE FROM DATE_DIMENSION;
--Check the condition to see if the start year is less than the end year (Input Parameters)
WHILE v_CURRENT_DATE <= v_END_DATE
LOOP
--DATE_DIMENSION Table
INSERT INTO DATE_DIMENSION
(
DATE_KEY,
FULL_DATE_DESCRIPTION,
DAY_OF_WEEK,
DAY_OF_MONTH,
DAY_OF_YEAR,
LAST_DAY_OF_WEEK_INDICATOR,
LAST_DAY_OF_MONTH_INDICATOR,
WEEK_ENDING_DATE,
MONTH_NUMBER,
MONTH_NAME,
YEAR_MONTH,
QUARTER_NUMBER,
YEAR_QUARTER,
YEAR_NUMBER
)
VALUES
(
v_CURRENT_DATE, --DATE_KEY
TO_CHAR(v_CURRENT_DATE, 'Day, Month DD, YYYY'), --FULL_DATE_DESCRIPTION
TO_NUMBER(TO_CHAR(v_CURRENT_DATE, 'D')) -1, --DAY_OF_WEEK
TO_CHAR(v_CURRENT_DATE,'DD'), --DAY_OF_MONTH
TO_CHAR(v_CURRENT_DATE,'DDD'), --DAY_OF_YEAR
CASE --LAST_DAY_OF_WEEK_INDICATOR
WHEN TO_CHAR(v_CURRENT_DATE,'FMDay') = 'Saturday' THEN 'Y'
ELSE 'N'
END,
CASE --LAST_DAY_OF_MONTH_INDICATOR
WHEN LAST_DAY(TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY')) = TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY') THEN 'Y'
ELSE 'N'
END,
CASE --WEEK_ENDING_DATE OF CURRENT WEEK ENDING ON SATURDAY
WHEN TO_CHAR(v_CURRENT_DATE,'FMDay') = 'Saturday' THEN v_CURRENT_DATE
ELSE NEXT_DAY(v_CURRENT_DATE,'SATURDAY')
END,
TO_CHAR(v_CURRENT_DATE,'MM'), --MONTH_NUMBER
TO_CHAR(v_CURRENT_DATE,'MONTH'), --MONTH_NAME
TO_CHAR(v_CURRENT_DATE,'MONTH YYYY'), --YEAR_MONTH
TO_CHAR(v_CURRENT_DATE,'Q'), --QUARTER_NUMBER
TO_CHAR(v_CURRENT_DATE,'YYYY Q'), --YEAR_QUARTER
TO_CHAR(v_CURRENT_DATE,'YYYY') --YEAR_NUMBER
);
--Increment and assign the current date value to be re-evaluated
v_CURRENT_DATE := v_CURRENT_DATE + 1;
END LOOP;
END;
仅供参考 - 这段代码解决了我的问题,使其使用 SQL Plus 执行。
CASE --LAST_DAY_OF_MONTH_INDICATOR
WHEN LAST_DAY(v_CURRENT_DATE) = v_CURRENT_DATE THEN 'Y'
ELSE 'N'
END,
您正在这样做:
WHEN LAST_DAY(TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY'))
= TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY') THEN 'Y'
但是 v_current_date
已经是 DATE 类型,所以对于 to_date
的这两个调用,您实际上是在做 to_date(to_char(v_current_date), 'MM/DD/YYYY')
,而 to_char
正在使用您的会话 NLS_DATE_FORMAT - 大概是 Apex 中的 MM/DD/YYYY,但 SQL*Plus 中的其他内容。
你没有显示你的实际错误,所以我有点猜测,但你实际上是在做类似的事情:
to_date(to_char(v_current_date, 'DD/MM/YYYY'), 'MM/DD/YYYY')
这有时会奏效,但如果月份中的某天在 12 号之后,则会出现无效月份错误,因为它会调换月份和日期数字。或者您的 NLS 设置可能会使用 MON,这会得到相同的错误,因为 Oracle 非常宽容地使用名称而不是月份数字。或者其他一些会给出不同错误的格式 - 有几种你可以点击。
您的修复程序仅使用 WHEN LAST_DAY(v_CURRENT_DATE) = v_CURRENT_DATE
,避免了显式转换为日期,也避免了从日期到字符串的隐式转换,因此您的 NLS 设置没有影响。
听起来你是在说两个环境都没有抛出编译错误,但有一个环境抛出了 运行时间错误。在那种情况下,我猜想您编写的代码取决于两种环境中不同的环境设置。
看看你的 LAST_DAY_OF_WEEK_INDICATOR
,这实际上就是你将 DATE
传递给 TO_DATE
所做的。从功能上讲,这没有意义,to_date
不接受 DATE
作为参数,它只接受 VARCHAR2
。当你打电话
TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY')
因此,Oracle 必须做一些事情。
首先,它将 v_current_date
隐式转换为字符串。因为它是隐式转换,所以它将使用您会话的 NLS_DATE_FORMAT
设置。数据库中的每个会话都可能有不同的 NLS_DATE_FORMAT
并且单个会话的 NLS_DATE_FORMAT
会随着时间的推移而改变,因此这种隐式转换的行为在编译时是未知的。如果您的 NLS_DATE_FORMAT
是 DD-MON-RR
,如果您在英文 Windows 机器上安装 Oracle 客户端,这是默认设置,则传递给 to_date
的字符串将是“20-FEB-15”(假设您今天调用了程序)。如果您的 NLS_DATE_FORMAT
是 DD/MM/YYYY
,则传递给 to_date
的字符串将是“20/02/2015”。
接下来,它调用 to_date
传入刚刚生成的字符串和您指定的格式掩码。如果您恰好在 NLS_DATE_FORMAT
与您传递给 to_date
的格式掩码匹配的会话中,您将从 to_date
返回相同的 date
。但是,如果不匹配,您可能会收到错误消息(DD-MON-YYYY 格式的字符串永远不会转换为 MM/DD/YYYY 格式的有效日期)或者您可能会收到意外结果(字符串格式 DD/MM/YYYY 可以使用 MM/DD/YYYY 格式掩码转换为有效日期,但该日期不会与您开始使用的日期相同——例如,3 月 1 日将转换为 1 月 3 日) .
如果您希望代码在任何环境下都运行正确,请避免隐式转换。
我在线使用Application Express(安装在Win7 32位上的OracleXE 11g Express Edition),下面的存储过程编译执行成功,没有错误。在 SQL Plus 命令行编译时,代码编译成功,但是当我执行时,它给我一个错误。现在我已经解决了错误(如下所示的代码片段),我的问题是为什么 Application Express 中的数据库引擎不抛出错误?希望我能正确解释我的问题。
Table定义
CREATE TABLE DATE_DIMENSION
(
DATE_KEY DATE NOT NULL,
FULL_DATE_DESCRIPTION VARCHAR2(64) NOT NULL,
DAY_OF_WEEK NUMBER(1,0) NOT NULL,
DAY_OF_MONTH NUMBER(2,0) NOT NULL,
DAY_OF_YEAR NUMBER(3,0) NOT NULL,
LAST_DAY_OF_WEEK_INDICATOR CHAR(1) NOT NULL,
LAST_DAY_OF_MONTH_INDICATOR CHAR(1) NOT NULL,
WEEK_ENDING_DATE DATE NOT NULL,
MONTH_NUMBER NUMBER(2,0) NOT NULL,
MONTH_NAME VARCHAR2(32) NOT NULL,
YEAR_MONTH CHAR(32) NOT NULL,
QUARTER_NUMBER NUMBER(1,0) NOT NULL,
YEAR_QUARTER CHAR(32) NOT NULL,
YEAR_NUMBER NUMBER(4,0) NOT NULL,
CONSTRAINT DATE_DIMENSION_PK PRIMARY KEY (DATE_KEY)
)
/
存储过程
create or replace PROCEDURE sp_DATE_DIMENSION(v_START_YEAR IN INT, v_END_YEAR IN INT) AS
--Declare two variables as DATE datatypes
v_CURRENT_DATE DATE;
v_END_DATE DATE;
BEGIN
--Assign the start year and end year to it's respective variables
v_CURRENT_DATE := TO_DATE('0101' || v_START_YEAR, 'MMDDYYYY');
v_END_DATE := TO_DATE('1231' || v_END_YEAR, 'MMDDYYYY');
--Clear/Dump what is currently stored in the table
DELETE FROM DATE_DIMENSION;
--Check the condition to see if the start year is less than the end year (Input Parameters)
WHILE v_CURRENT_DATE <= v_END_DATE
LOOP
--DATE_DIMENSION Table
INSERT INTO DATE_DIMENSION
(
DATE_KEY,
FULL_DATE_DESCRIPTION,
DAY_OF_WEEK,
DAY_OF_MONTH,
DAY_OF_YEAR,
LAST_DAY_OF_WEEK_INDICATOR,
LAST_DAY_OF_MONTH_INDICATOR,
WEEK_ENDING_DATE,
MONTH_NUMBER,
MONTH_NAME,
YEAR_MONTH,
QUARTER_NUMBER,
YEAR_QUARTER,
YEAR_NUMBER
)
VALUES
(
v_CURRENT_DATE, --DATE_KEY
TO_CHAR(v_CURRENT_DATE, 'Day, Month DD, YYYY'), --FULL_DATE_DESCRIPTION
TO_NUMBER(TO_CHAR(v_CURRENT_DATE, 'D')) -1, --DAY_OF_WEEK
TO_CHAR(v_CURRENT_DATE,'DD'), --DAY_OF_MONTH
TO_CHAR(v_CURRENT_DATE,'DDD'), --DAY_OF_YEAR
CASE --LAST_DAY_OF_WEEK_INDICATOR
WHEN TO_CHAR(v_CURRENT_DATE,'FMDay') = 'Saturday' THEN 'Y'
ELSE 'N'
END,
CASE --LAST_DAY_OF_MONTH_INDICATOR
WHEN LAST_DAY(TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY')) = TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY') THEN 'Y'
ELSE 'N'
END,
CASE --WEEK_ENDING_DATE OF CURRENT WEEK ENDING ON SATURDAY
WHEN TO_CHAR(v_CURRENT_DATE,'FMDay') = 'Saturday' THEN v_CURRENT_DATE
ELSE NEXT_DAY(v_CURRENT_DATE,'SATURDAY')
END,
TO_CHAR(v_CURRENT_DATE,'MM'), --MONTH_NUMBER
TO_CHAR(v_CURRENT_DATE,'MONTH'), --MONTH_NAME
TO_CHAR(v_CURRENT_DATE,'MONTH YYYY'), --YEAR_MONTH
TO_CHAR(v_CURRENT_DATE,'Q'), --QUARTER_NUMBER
TO_CHAR(v_CURRENT_DATE,'YYYY Q'), --YEAR_QUARTER
TO_CHAR(v_CURRENT_DATE,'YYYY') --YEAR_NUMBER
);
--Increment and assign the current date value to be re-evaluated
v_CURRENT_DATE := v_CURRENT_DATE + 1;
END LOOP;
END;
仅供参考 - 这段代码解决了我的问题,使其使用 SQL Plus 执行。
CASE --LAST_DAY_OF_MONTH_INDICATOR
WHEN LAST_DAY(v_CURRENT_DATE) = v_CURRENT_DATE THEN 'Y'
ELSE 'N'
END,
您正在这样做:
WHEN LAST_DAY(TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY'))
= TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY') THEN 'Y'
但是 v_current_date
已经是 DATE 类型,所以对于 to_date
的这两个调用,您实际上是在做 to_date(to_char(v_current_date), 'MM/DD/YYYY')
,而 to_char
正在使用您的会话 NLS_DATE_FORMAT - 大概是 Apex 中的 MM/DD/YYYY,但 SQL*Plus 中的其他内容。
你没有显示你的实际错误,所以我有点猜测,但你实际上是在做类似的事情:
to_date(to_char(v_current_date, 'DD/MM/YYYY'), 'MM/DD/YYYY')
这有时会奏效,但如果月份中的某天在 12 号之后,则会出现无效月份错误,因为它会调换月份和日期数字。或者您的 NLS 设置可能会使用 MON,这会得到相同的错误,因为 Oracle 非常宽容地使用名称而不是月份数字。或者其他一些会给出不同错误的格式 - 有几种你可以点击。
您的修复程序仅使用 WHEN LAST_DAY(v_CURRENT_DATE) = v_CURRENT_DATE
,避免了显式转换为日期,也避免了从日期到字符串的隐式转换,因此您的 NLS 设置没有影响。
听起来你是在说两个环境都没有抛出编译错误,但有一个环境抛出了 运行时间错误。在那种情况下,我猜想您编写的代码取决于两种环境中不同的环境设置。
看看你的 LAST_DAY_OF_WEEK_INDICATOR
,这实际上就是你将 DATE
传递给 TO_DATE
所做的。从功能上讲,这没有意义,to_date
不接受 DATE
作为参数,它只接受 VARCHAR2
。当你打电话
TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY')
因此,Oracle 必须做一些事情。
首先,它将 v_current_date
隐式转换为字符串。因为它是隐式转换,所以它将使用您会话的 NLS_DATE_FORMAT
设置。数据库中的每个会话都可能有不同的 NLS_DATE_FORMAT
并且单个会话的 NLS_DATE_FORMAT
会随着时间的推移而改变,因此这种隐式转换的行为在编译时是未知的。如果您的 NLS_DATE_FORMAT
是 DD-MON-RR
,如果您在英文 Windows 机器上安装 Oracle 客户端,这是默认设置,则传递给 to_date
的字符串将是“20-FEB-15”(假设您今天调用了程序)。如果您的 NLS_DATE_FORMAT
是 DD/MM/YYYY
,则传递给 to_date
的字符串将是“20/02/2015”。
接下来,它调用 to_date
传入刚刚生成的字符串和您指定的格式掩码。如果您恰好在 NLS_DATE_FORMAT
与您传递给 to_date
的格式掩码匹配的会话中,您将从 to_date
返回相同的 date
。但是,如果不匹配,您可能会收到错误消息(DD-MON-YYYY 格式的字符串永远不会转换为 MM/DD/YYYY 格式的有效日期)或者您可能会收到意外结果(字符串格式 DD/MM/YYYY 可以使用 MM/DD/YYYY 格式掩码转换为有效日期,但该日期不会与您开始使用的日期相同——例如,3 月 1 日将转换为 1 月 3 日) .
如果您希望代码在任何环境下都运行正确,请避免隐式转换。