我有下表记录了员工在公司的出勤情况。
CREATE TABLE EMP_ATNDNC
(
WORK_DAY DATE NOT NULL,
EMP_ID VARCHAR2(20) NOT NULL,
STATUS VARCHAR2(1) DEFAULT '0' NOT NULL,
TMSTMP TIMESTAMP,
RCRD_VER TIMESTAMP NOT NULL,
CONSTRAINT EMP_ATNDNC_PK PRIMARY KEY(WORK_DAY,EMP_ID),
CONSTRAINT EMP_ATNDNC_FK_DAY FOREIGN KEY(WORK_DAY) REFERENCES SCHOOL_OPEN_RCRD(WORK_DAY),
CONSTRAINT EMP_ATNDNC_FK_ID FOREIGN KEY(EMP_ID) REFERENCES EMP_RCRD(EMP_ID)
);
截至目前,它有两条记录:
WORK_DAY EMP_ID STATUS TMSTMP RCRD_VER
29-SEP-16 1234 0 27-AUG-16 05.38.46.022000000 PM 27-AUG-16 05.38.46.022000000 PM
26-AUG-16 9999 0 26-AUG-16 04.50.04.628000000 PM 26-AUG-16 04.50.04.628000000 PM
如果我运行此查询:
SELECT * FROM EMP_ATNDNC WHERE WORK_DAY BETWEEN TO_DATE('24-AUG-16') AND TO_DATE('30-SEP-16');
我只获得此记录:
WORK_DAY EMP_ID STATUS TMSTMP RCRD_VER
29-SEP-16 1234 0 27-AUG-16 05.38.46.022000000 PM 27-AUG-16 05.38.46.022000000 PM
此外,如果我运行此查询:
SELECT * FROM EMP_ATNDNC ORDER BY WORK_DAY ASC;
我得到了这个结果:
WORK_DAY EMP_ID STATUS TMSTMP RCRD_VER
29-SEP-16 1234 0 27-AUG-16 05.38.46.022000000 PM 27-AUG-16 05.38.46.022000000 PM
26-AUG-16 9999 0 26-AUG-16 04.50.04.628000000 PM 26-AUG-16 04.50.04.628000000 PM
而不是:
WORK_DAY EMP_ID STATUS TMSTMP RCRD_VER
26-AUG-16 9999 0 26-AUG-16 04.50.04.628000000 PM 26-AUG-16 04.50.04.628000000 PM
29-SEP-16 1234 0 27-AUG-16 05.38.46.022000000 PM 27-AUG-16 05.38.46.022000000 PM
为什么会这样?
答案 0 :(得分:3)
您似乎的日期不在本世纪,可能是因为它们插入的格式模型不正确。 (没有任何理由继续使用两位数的年份......)
使用表格定义演示:
insert into EMP_ATNDNC
values (to_date('26-AUG-16', 'DD-MON-RRRR'), 9999, 0,
to_timestamp('26-AUG-16 04.50.04.628000000 PM', 'DD-MON-RR HH.MI.SS.FF AM'),
to_timestamp('26-AUG-16 04.50.04.628000000 PM', 'DD-MON-RR HH.MI.SS.FF AM'));
insert into EMP_ATNDNC
values (to_date('29-SEP-16', 'DD-MON-YYYY'), 1234, 0,
to_timestamp('27-AUG-16 05.38.46.022000000 PM', 'DD-MON-RR HH.MI.SS.FF AM'),
to_timestamp('27-AUG-16 05.38.46.022000000 PM', 'DD-MON-RR HH.MI.SS.FF AM'));
alter session set nls_date_format = 'DD-MON-RR';
alter session set nls_timestamp_format = 'DD-MON-RR HH:MI:SS.FF3 AM';
SELECT * FROM EMP_ATNDNC WHERE WORK_DAY BETWEEN TO_DATE('24-AUG-16') AND TO_DATE('30-SEP-16');
WORK_DAY EMP_ID S TMSTMP RCRD_VER
--------- -------------------- - ------------------------- -------------------------
26-AUG-16 9999 0 26-AUG-16 04:50:04.628 PM 26-AUG-16 04:50:04.628 PM
WORK_DAY EMP_ID S TMSTMP RCRD_VER
--------- -------------------- - ------------------------- -------------------------
29-SEP-16 1234 0 27-AUG-16 05:38:46.022 PM 27-AUG-16 05:38:46.022 PM
26-AUG-16 9999 0 26-AUG-16 04:50:04.628 PM 26-AUG-16 04:50:04.628 PM
如果你看两个插入语句,就会使用RRRR转换两位数的年份,这会产生一些(通常是有用的)关于本世纪的假设,在这种情况下将'16'视为'2016';第二种是使用YYYY,这是更严厉的。
如果使用全年格式模型查询实际日期,您将看到差异:
select to_char(work_day, 'SYYYY-MM-DD') as work_day, emp_id from emp_atndnc order by work_day;
WORK_DAY EMP_ID
----------- --------------------
0016-09-29 1234
2016-08-26 9999
问题不在于您的查询(尽管使用两位数年份和隐式转换也依赖于您的会话的NLS设置,@ TimBiegeleisen指出这不是一个好主意。)
真正的问题在于数据。您可以纠正它 - 如果您也愿意做出假设,例如在本世纪之前增加了2000年。
但实际上你还需要找出坏数据的来源,并阻止它发生。您可能有一个依赖于字符串隐式日期转换的插入,并且不同的用户或应用程序具有不同的NLS设置 - 特别是NLS_DATE_FORMAT。
您永远不应该依赖依赖于NLS设置的隐式数据转换。你不应该再使用两位数的年份了,尽管这可能超出了你的控制范围。
答案 1 :(得分:1)
//CHANGED
子句中的第二个日期将年份指定为两位数字:
#include <iostream>
#include <string>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/phoenix.hpp>
using namespace boost::spirit;
namespace phx = boost::phoenix;
enum token_id //ADDED
{
ID_ELLIPSES = lex::min_token_id + 1,
ID_NUMBER
};
///////////////////////////////////////////////////////////////////////////////
// Token definition
///////////////////////////////////////////////////////////////////////////////
template <typename Lexer>
struct example3_tokens : lex::lexer<Lexer>
{
example3_tokens()
{
// define the tokens to match
ellipses = "\\.\\.\\.";
number = lex::token_def<>("[0-9]+", ID_NUMBER); //CHANGED
ellipses.id(ID_ELLIPSES); //CHANGED
// associate the tokens and the token set with the lexer
this->self = ellipses[phx::ref(std::cout) << "Found ellipses.\n"] | '(' | ')' | number[phx::ref(std::cout) << "Found: " << phx::construct<std::string>(lex::_start, lex::_end) << '\n']; //CHANGED
// define the whitespace to ignore (spaces, tabs, newlines and C-style
// comments)
this->self("WS")
= lex::token_def<>("[ \\t\\n]+") // whitespace
| "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/" // C style comments
;
}
// these tokens expose the iterator_range of the matched input sequence
lex::token_def<> ellipses, identifier, number;
};
///////////////////////////////////////////////////////////////////////////////
// Grammar definition
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator, typename Lexer>
struct example3_grammar
: qi::grammar<Iterator, qi::in_state_skipper<Lexer> >
{
template <typename TokenDef>
example3_grammar(TokenDef const& tok)
: example3_grammar::base_type(start)
{
start
= +(couplet | qi::token(ID_ELLIPSES)) //CHANGED
;
// A couplet matches nested left and right parenthesis.
// For example:
// (1) (1 2) (1 2 3) ...
// ((1)) ((1 2)(3 4)) (((1) (2 3) (1 2 (3) 4))) ...
// (((1))) ...
couplet
= qi::token(ID_NUMBER) //CHANGED
| '(' >> +couplet >> ')'
;
BOOST_SPIRIT_DEBUG_NODE(start);
BOOST_SPIRIT_DEBUG_NODE(couplet);
}
qi::rule<Iterator, qi::in_state_skipper<Lexer> > start, couplet;
};
///////////////////////////////////////////////////////////////////////////////
int main()
{
// iterator type used to expose the underlying input stream
typedef std::string::iterator base_iterator_type;
// This is the token type to return from the lexer iterator
typedef lex::lexertl::token<base_iterator_type> token_type;
// This is the lexer type to use to tokenize the input.
// Here we use the lexertl based lexer engine.
typedef lex::lexertl::actor_lexer<token_type> lexer_type; //CHANGED
// This is the token definition type (derived from the given lexer type).
typedef example3_tokens<lexer_type> example3_tokens;
// this is the iterator type exposed by the lexer
typedef example3_tokens::iterator_type iterator_type;
// this is the type of the grammar to parse
typedef example3_grammar<iterator_type, example3_tokens::lexer_def> example3_grammar;
// now we use the types defined above to create the lexer and grammar
// object instances needed to invoke the parsing process
example3_tokens tokens; // Our lexer
example3_grammar calc(tokens); // Our parser
std::string str ="(1) (1 2) (1 2 3) ... ((1)) ((1 2)(3 4)) (((1) (2 3) (1 2 (3) 4))) ... (((1))) ..."; //CHANGED
// At this point we generate the iterator pair used to expose the
// tokenized input stream.
std::string::iterator it = str.begin();
iterator_type iter = tokens.begin(it, str.end());
iterator_type end = tokens.end();
// Parsing is done based on the token stream, not the character
// stream read from the input.
// Note how we use the lexer defined above as the skip parser.
bool r = qi::phrase_parse(iter, end, calc, qi::in_state("WS")[tokens.self]);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
std::cout << "Bye... :-) \n\n";
return 0;
}
假设您的默认日期格式为BETWEEN
,则进行以下更改可能有效:
BETWEEN TO_DATE('24-AUG-2016') AND TO_DATE('30-SEP-16');
^^
但是,正如dd-mon-yyyy
提到的documentation:
最好始终使用TO_DATE
指定格式掩码(fmt)
理想情况下,您应明确提及您使用的日期格式,例如:如在此查询中:
BETWEEN TO_DATE('24-AUG-2016') AND TO_DATE('30-SEP-2016');