CUST_TRANS -
Name Null? Type
-------------- ----------------- ------------------
CUSTNO NOT NULL CHAR (2)
TRANSDATE DATE
TRANSAMT NUMBER (6, 2)
CUSTNO TRANSDATE TRANSAMT
------------- ----------------------- -----------------------
11 01-JAN-07 1000
22 01-FEB-07 2000
33 01-MAR-07 3000
Dates are stored in the default date format dd-mm-rr in the CUST_TRANS table.
Which three SQL statements would execute successfully? (Choose three.)
A. SELECT transdate + '10' FROM cust_trans;
B. SELECT * FROM cust_trans WHERE transdate = '01-01-07';
C. SELECT transamt FROM cust_trans WHERE custno > '11';
D. SELECT * FROM cust_trans WHERE transdate='01-JANUARY-07';
E. SELECT custno + 'A' FROM cust_trans WHERE transamt > 2000;
我正在研究这个问题,我在sqldeveloper中创建了表,我尝试在B中运行相同的代码,它很有效。所以我想知道为什么答案B被认为是错误的。
INSERT ALL
INTO CUST_TRANS VALUES (11, '01-jAN-07', 1000)
INTO CUST_TRANS VALUES (22, '01-FEB-07', 2000)
INTO CUST_TRANS VALUES (33, '01-MAR-07', 3000)
SELECT * FROM DUAL;
我做的桌子。我试着运行的脚本
SELECT * FROM cust_trans WHERE transdate = '01-01-07';
它在sqldeveloper中成功运行,我正在运行最新的oracle版本
NLS_LANGUAGE ENGLISH
NLS_TERRITORY CANADA
NLS_CURRENCY $
NLS_ISO_CURRENCY CANADA
NLS_NUMERIC_CHARACTERS .,
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT RR-MM-DD
NLS_DATE_LANGUAGE ENGLISH
NLS_SORT BINARY
NLS_TIME_FORMAT HH24:MI:SSXFF
NLS_TIMESTAMP_FORMAT RR-MM-DD HH24:MI:SSXFF
NLS_TIME_TZ_FORMAT HH24:MI:SSXFF TZR
NLS_TIMESTAMP_TZ_FORMAT RR-MM-DD HH24:MI:SSXFF TZR
NLS_DUAL_CURRENCY $
NLS_COMP BINARY
NLS_LENGTH_SEMANTICS BYTE
NLS_NCHAR_CONV_EXCP FALSE
我的配置
日期以默认日期格式
dd-mm-rr
存储在CUST_TRANS
表中。
这句话错了!DATE
是一种二进制数据类型,由7个字节组成,表示:世纪、世纪年、月、日、小时、分钟和秒。它是与所有这些组件一起存储的,并且是以任何特定(人类可读)格式存储的,因为它存储为二进制值。
如果以dd-mm-rr
格式存储类似日期的数据,则将它们存储为字符串,而不是DATE
。如果将日期存储为DATE
,则不能将其存储为可读格式。
如果您想了解Oracle如何存储DATE
,请使用:
SELECT DUMP(transdate) FROM cust_trans;
设置问题的人可能的意思是,dd-mm-rr
是Oracle用于字符串到日期和日期到字符串转换的默认格式(使用NLS_DATE_FORMAT
会话参数),并且一些客户端应用程序(即SQL*Plus和SQLDeveloper)在显示日期时用作默认显示格式。然而,仅仅因为某些会话参数允许字符串的隐式转换,并不意味着这是一种好的做法。
指出dd-mm-rr
是默认日期格式也是错误的,因为它只是某些地区(阿尔及利亚、巴林、印度、摩洛哥、荷兰和突尼斯)的NLS_DATE_FORMAT
会话参数的默认格式;在世界其他地方,它不是默认的。
B。
SELECT * FROM cust_trans WHERE transdate = '01-01-07';
我试着在B中运行相同的代码,它很有效。所以我想知道为什么答案B被认为是错误的。
B。只要Oracle可以执行从字符串到DATE
的隐式强制转换,并且它使用NLS_DATE_FORMAT
会话来执行该转换,就可以工作。
代码实际上与相同
SELECT *
FROM cust_trans
WHERE transdate = TO_DATE(
'01-01-07',
( SELECT value
FROM NLS_SESSION_PARAMETERS
WHERE PARAMETER = 'NLS_DATE_FORMAT' )
);
如果NLS_DATE_FORMAT
与dd-mm-rr
(或字符串到日期转换规则的另一种格式)匹配,则它将对其进行解析
如果你这样做:
ALTER SESSION SET NLS_DATE_FORMAT = 'dd-mm-rr';
SELECT * FROM cust_trans WHERE transdate = '01-01-07'
然后,日期将被解析为2007-01-01 00:00:00
,但如果您使用:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
SELECT * FROM cust_trans WHERE transdate = '01-01-07'
然后,日期将被解析为0001-01-07 00:00:00
,它有一个意想不到的世纪,年份和日期被交换。
如果您使用:
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-RR';
SELECT * FROM cust_trans WHERE transdate = '01-01-07'
然后,它不解析该值,并引发异常ORA-01843: not a valid month
。
db<gt;小提琴这里
您应该NEVER依赖字符串到日期的隐式强制转换。
更好的解决方案是:
使用日期文字
SELECT * FROM cust_trans WHERE transdate = DATE '2007-01-01'
使用时间戳文字
SELECT * FROM cust_trans WHERE transdate = TIMESTAMP '2007-01-01 00:00:00'
或者,使用显式字符串进行日期转换
SELECT * FROM cust_trans WHERE transdate = TO_DATE('01-01-07', 'DD-MM-RR')
因为这些都不依赖于数据类型之间的隐式强制转换。
关于这个问题的答案。
如果NLS_DATE_FORMAT
是DD-MM-RR
,那么四个(而不是三个)选项是将成功解析和执行的查询。
给定设置:
CREATE TABLE cust_trans (
custno CHAR(2),
transdate DATE,
transamt NUMBER
);
INSERT INTO cust_trans (custno, transdate, transamt) VALUES ('25', DATE '2007-01-01', 5000);
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MM-RR';
然后:
A。
SELECT transdate + '10' FROM cust_trans;
将执行从字符串文字
'10'
到数字10
的隐式转换,然后将向transdate
添加10天并输出:TRANSDATE+'10' 11-07 您有一个名为nls_date_format的东西。您可以通过更改会话来设置它。
当你把它设置成这样的问题格式时:
ALTER SESSION SET NLS_DATE_FORMAT = 'dd-mm-rr';
B的答案很好。
当您将其设置为,例如:
ALTER SESSION SET NLS_DATE_FORMAT = 'DD MON YYYY';
它不会起作用。
演示
您可以通过以下查询检查此参数在会话中的设置方式:
select * from nls_session_parameters where parameter = 'NLS_DATE_FORMAT';