1996年初,歐洲安全電子市場(EU SEMPER)項目組決定編寫自己的日志記錄API,后來這個API演變成了Log4j。Log4j是一個開放源碼項目,一個非常流行的Java日志記錄包。它允許開發者向代碼中插入日志記錄語句,還允許在不修改應用程序源碼的情況下修改日志記錄的行為。
幾乎每一個項目都會使用日志記錄,但是由于日志記錄不是項目的核心,因此受重視的程度一般不是很高。我們認為使用日志記錄是一件非常嚴肅的事情,而且做好使用日志記錄的規劃比單純記錄日志本身更加重要。
本文將比較全面的闡述Log4j的設計原理和使用方法。
日志記錄記錄的是應用程序運行的軌跡。我們可以通過查看這些軌跡來調試應用程序,這可能也是日志記錄最為流行的用法了。但是我們必須意識到規劃良好的日志記錄中還含有豐富的信息,通過手工的方式或借助一些工具(大多數時候需要自己來書寫這些工具)來分析挖掘這些信息。
例如,如果我們在規劃中指出必須記錄用戶的每一次操作,記錄的樣式為 [日志信息]-[操作開始的時間]-[日志級別]-[日志類別]-[用戶名]-[操作名]-[消息],這只是我們假設的一種樣式,實際的日志中一般會含有比這更加豐富的信息。為了更好的理解,我們根據該樣式構造了一些日志記錄(其中日志類別org.solol.Main、org.solol.Parser和org.solol.UserOperator使用了不同的樣式):
[日志信息]-[2006-07-30 08:54:20]-[INFO]-[org.solol.Main]-[具體的消息] [日志信息]-[2006-07-30 08:55:20]-[INFO]-[org.solol.UserOperator]-[User1]-[查詢報表1]-[具體的消息] [日志信息]-[2006-07-30 08:55:30]-[INFO]-[org.solol.UserOperator]-[User1]-[查詢報表2]-[具體的消息] [日志信息]-[2006-07-30 08:56:01]-[INFO]-[org.solol.Parser]-[具體的消息] [日志信息]-[2006-07-30 08:57:26]-[INFO]-[org.solol.UserOperator]-[User2]-[添加用戶User3]-[具體的消息] [日志信息]-[2006-07-30 08:58:20]-[INFO]-[org.solol.UserOperator]-[User1]-[查詢報表3]-[具體的消息] [日志信息]-[2006-07-30 08:59:38]-[INFO]-[org.solol.UserOperator]-[User3]-[查詢報表1]-[具體的消息] [日志信息]-[2006-07-30 08:59:39]-[INFO]-[org.solol.UserOperator]-[User2]-[退出系統]-[具體的消息]
從上面的日志記錄中我們很容易抽取出某一用戶的操作列表,如對于用戶User1我們的結果為:
[日志信息]-[2006-07-30 08:55:20]-[INFO]-[org.solol.UserOperator]-[User1]-[查詢報表1]-[具體的消息] [日志信息]-[2006-07-30 08:55:30]-[INFO]-[org.solol.UserOperator]-[User1]-[查詢報表2]-[具體的消息] [日志信息]-[2006-07-30 08:58:20]-[INFO]-[org.solol.UserOperator]-[User1]-[查詢報表3]-[具體的消息]
這樣我們就得到了某一時間段中User1的操作列表,可以利用這一列表來進行安全分析。
我們還可以從另外的角度來分析上面的日志記錄,如我們很容易統計出操作(日志類別為org.solol.UserOperator)發生的總次數(6次),其中操作[查詢報表1]為2次,[查詢報表2]為1次,[查詢報表3]為1次,[添加用戶User3]為1次,[退出系統]為1次。這樣我們就可以得出系統中的那些操作用戶使用的比較頻繁。
以上我們從兩個角度對日記記錄中的信息進行了簡單的挖掘,實際中待挖掘的方面要豐富的多,這取決于您的意圖和您的想象力。
這里我們還要特別強調一下:所有這一切都需要有使用日志記錄的良好規劃。如果規劃不好(即日志記錄沒有規律性),那么我們挖掘時的任務就會非常繁重或者使挖掘成為一個不可能的任務。
文章到了這里我們要來描述日志記錄的最為流行的用法了,即調試應用程序。我們在調試應用程序時一般會使用兩種方法,除了日志記錄之外,還有debugger調試器。
我們不想把他們放到一起來描述,因為這是兩個完全不同的問題,雖然他們都用來調試應用程序。使用debugger調試器我們可以清楚的知道引發錯誤的上下文及其相關信息,也可以使用單步執行、設置斷點、檢查變量以及暫掛和恢復線程等等比較高級的能力,但是盡管這樣它也不能替代日志記錄,同樣日志記錄也不能替代debugger調試器。我們要結合使用這兩種方法,不同的場景使用不同的方法會有更好的效果。
我們認為使用日志記錄來調試應用程也應該充分考慮軟件的開發周期。這里我們只考慮軟件開發周期中的與日志記錄有關的兩個階段:
閱讀到這里我們就應該著手實現我們的日志記錄了。比較幸運的是我們有好多日志記錄軟件包可選,這就使我們不必關心日志記錄的細節,只要把主要的精力放到日志記錄的規劃上就好了。我們選擇的是Log4j,文章的余下部分將主要介紹這個Java日志記錄軟件包。
log4j的特性列表:
Log4j有三個主要的組件:Logger、Appender和Layout。這三個組件相互配合使得我們可以獲得非常強大的日志記錄的能力。
Logger的名稱是區分大小寫的,依據名稱可以確定其層次結構(即父子關系),規則如下:
在Logger的層次結構的最頂層是root logger,它會永遠存在,而且不能通過名字取到。
上面文字的描述可能不好的理解,為此我們給出了一張圖,Logger的層次結構圖,從中可以非常直觀的看出三種主要組件的關系和各自所起的作用。
Loger x.y是Logger x.y.z的祖先,因為x.y.是x.y.z的前綴,這符合規則的前一條。另外在Logger x.y和Logger x.y.z之間,Logger x.y.z沒有其它的祖先,因此Logger x.y是Logger x.y.z的父親,這符合規則的后一條。這樣我們依據上面的規則就可以構造出如圖1所示的Logger的層次結構。
從圖1中我們還可以看到每一個Logger都有一個Level,根據該Level的值Logger決定是否處理對應的日志請求。如果Level沒有被設置,就象圖1中的Logger x.y一樣,又該怎么辦呢?答案是可以從祖先那里繼承。
如果Logger C沒有被設置Level,那么它將沿著它的層次結構向上查找,如果找到就繼承并結束,否則會一直查找到root logger結束。因為log4j在設計時保證root logger會被設置一個默認的Level,所以任何logger都可以繼承到Level。
圖1中的Logger x.y沒有被設置Level,但是根據上面的繼承規則,Logger x.y繼承了root logger的Level。
我們在來看看Logger選擇日志記錄請求(log request)的規則:
假設Logger M具有q級的Level,這個Level可能是設置的也可能是繼承到的。
如果向Logger M發出一個Level為p的日志記錄請求,那么只有滿足p>=q時這個日志記錄請求才會被處理。
org.apache.log4j.Logger中的不同方法發出不同Level的日志記錄請求,如下:
其中的靜態常量DEBUG、INFO、WARN、ERROR、FATAL是在org.apache.log4j.Level中定義的,除了使用這些預定義的Level之外,Log4j還支持自定義Level。
注:org.apache.log4j.Level中還預定義了一些其它的Level。
在Log4j中,Appender指的是日志記錄輸出的目的地。當前支持的Appender(目的地)有文件(file)、控制臺(console)、java.io.OutputStream、java.io.Writer、遠程服務器、遠程Unix Syslog守護者、遠程JMS監聽者、NT EventLog或者發送e-mail。如果您在上面沒有找到適合的Appender,那就需要考慮實現自己的自定義Appender了。
每個Logger可以有多個Appender,但是相同的Appender只會被添加一次。
Appender的附加性意味著Logger C會將日志記錄發給它的和它祖先的所有Appender。在圖1中Logger a會將日志記錄發給它自己的JDBCAppender和它的祖先root logger的ConsoleAppender和FileAppender。Logger x.y.z自己沒有Appender,它將把日志記錄發給它的祖先root logger的ConsoleAppender和FileAppender,如果Logger x.y也含有Appender,那么它們也會包括在內。
Appender的附加性是可以被中斷的。假設Logger C的一個祖先為Logger P,如果Logger P的附加性標志(additivity flag)設置為假,那么Logger C會將日志記錄只發給它的和在它和Logger P之間的祖先(包括Logger P)的Appender,而不會發給Logger P的祖先的Appender。Logger的附加性標志(additivity flag)默認值為ture。
在圖1中如果沒有設置Logger a的附加性標志(additivity flag),而是使用默認值true,那么Logger a會將日志記錄發給它自己的JDBCAppender和它祖先root logger的ConsoleAppender和FileAppender,這和上面的描述相同。如果設置Logger a的附加性標志(additivity flag)的值false,那么Logger a會將日志記錄發給它自己的JDBCAppender而不會在發給它祖先root logger的ConsoleAppender和FileAppender了。
Appender定制了輸出目的地,通常我們還需要定制日志記錄的輸出格式,在Log4j中是通過將Layout和Appender關聯到一起來實現的。Layout依據用戶的要求來格式化日志記錄。PatternLayout(標準Log4j組件)讓用戶依據類似于C語言printf函數的轉換模式來指定輸出格式。
例如,轉換模式(conversion pattern)為"%r [%t] %-5p %c - %m%n"的PatternLayout將生成類似于以下內容的輸出:
176 [main] INFO org.foo.Bar - Located nearest gas station.
在上面的輸出中:
Log4j中還提到了一些其它的Layout,包括HTMLLayout、SimpleLayout、XMLLayout、TTCCLayout和DateLayout。如果這些不能滿足您的要求,還可以自定義自己的Layout。
依據既有的經驗顯示用于日志記錄的代碼大約是全部代碼量的4%。如果應用程序具有一定的規模,日志記錄語句的數量還是比較巨大的,因此必須有效的管理這些語句。
在Log4j中我們可以通過配置Log4j環境來有效的管理日志記錄。配置的方式有三種:
通過程序配置Log4j環境實際上就是在應用程序的代碼中改變Logger的Level或增加減少Appender等等。
Log4j提供了BasicConfigurator,它只是為root logger添加Appender。其中,
我們可以把BasicConfigurator看成是一個簡單的使用程序配置Log4j環境的示例。例如,要給root logger添加兩個Appender(A和B),下面的代碼分別完成了這個要求。
不使用BasicConfigurator:
//示例代碼,不能直接使用 Logger root = Logger.getRootLogger(); root.addAppender(A); root.addAppender(B);
使用BasicConfigurator:
//示例代碼,不能直接使用 BasicConfigurator.configure(A); BasicConfigurator.configure(B);
這里要使用PropertyConfigurator來分析配置文件并設置日志記錄,但是要注意日志記錄先前的配置不會被清除和重設。
Property文件是由key=value這樣的鍵值對所組成的,可以使用#或!作為注釋行的開始。下面給出了兩個簡單的示例:
非常簡單的示例1:
log4j.rootLogger=DEBUG, A1 log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%-4r %-5p [%t] %37c %3x - %m%n
稍顯復雜的示例2:
log4j.rootLogger=, A1, A2 log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n log4j.appender.A2=org.apache.log4j.FileAppender log4j.appender.A2.File=filename.log log4j.appender.A2.Append=false log4j.appender.A2.layout=org.apache.log4j.PatternLayout log4j.appender.A2.layout.ConversionPattern=%-5r %-5p [%t] %c{2} - %m%n
上面的兩個示例只是讓您對配置文件的格式有一個大體的認識,我們將在后面詳細的描述各個配置元素的語法。
Repository-wide threshold:
Repository-wide threshold指定的Level的優先級高于Logger本身的Level。語法為log4j.threshold=[level],level可以為OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL。也可以使用自定義Level,這時的語法為log4j.threshold=[level#classname]。默認為ALL。
依據上面的規則,我們有這樣的結論:如果log4j.threshold=ERROR,Logger C的Level=DEBUG,這時只有高于等于ERROR的日志記錄請求會被Logger C處理。
Appender的配置:
Appender的配置語法為
# For appender named appenderName, set its class. # Note: The appender name can contain dots. log4j.appender.appenderName=fully.qualified.name.of.appender.class # Set appender specific options. log4j.appender.appenderName.option1=value1 ... log4j.appender.appenderName.optionN=valueN #For each named appender you can configure its Layout. #The syntax for configuring an appender's layout is: log4j.appender.appenderName.layout=fully.qualified.name.of.layout.class log4j.appender.appenderName.layout.option1=value1 .... log4j.appender.appenderName.layout.optionN=valueN
Logger的配置:
root logger的配置語法:
log4j.rootLogger=[level], appenderName, appenderName, ...,其中level可以為OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL。也可以使用自定義Level,這時的語法為[level#classname]。
如果Level被指定那么root logger的Level將被配置為指定值。如果Level沒有被指定那么root logger的Level不會被修改。從上面的語法中我們可以看出通過用,分隔的列表可以為root logger指定多個Appender。
對于root logger之外的logger語法是相似的,為log4j.logger.logger_name=[level|INHERITED|NULL], appenderName, appenderName, ...
上面只有INHERITED和NULL需要說明一下,其它部分和root logger相同。INHERITED和NULL的意義是相同的。如果我們使用了它們,意味著這個logger將不在使用自己的Level而是從它的祖先那里繼承。
Logger的附加性標志(additivity flag)可以使用log4j.additivity.logger_name=[false|true]來配置。
ObjectRenderer配置:
我們可以通過ObjectRenderer來定義將消息對象轉換成字符串的方式。語法為log4j.renderer.fully.qualified.name.of.rendered.class=fully.qualified.name.of.rendering.class。如:
//my.Fruit類型的消息對象將由my.FruitRenderer轉換成字符串 log4j.renderer.my.Fruit=my.FruitRenderer
對上面的各個配置元素的語法理解之后,在來看示例1和2就很容易了。
PropertyConfigurator不支持Filter的配置。如果要支持Filter您可以使用DOMConfigurator,即使用XML文件的方式配置。
要使用DOMConfigurator.configure()來讀取XML格式的配置文件。XML文件格式的定義是通過org/apache/log4j/xml/log4j.dtd來完成的,各個配置元素的嵌套關系如下:
<!ELEMENT log4j:configuration (renderer*, appender*,(category|logger)*,root?,categoryFactory?)>
這里沒有給出更為詳細的內容,要了解詳細的內容需要查閱log4j.dtd。
下面這個簡單的示例可以使您對XML配置文件的格式有一個基本的認識:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE log4j SYSTEM "log4j.dtd"> <log4j> <appender name="A1" class="org.apache.log4j.FileAppender"> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%-5p %c{2} - %m\n"/> </layout> </appender> <appender name="A2" class="org.apache.log4j.FileAppender"> <layout class="org.apache.log4j.TTCCLayout"> <param name="DateFormat" value="ISO8601" /> </layout> <param name="File" value="warning.log" /> <param name="Append" value="false" /> </appender> <category name="org.apache.log4j.xml" priority="debug"> <appender-ref ref="A1" /> </category> <root priority="debug"> <appender-ref ref="A1" /> <appender-ref ref="A2" /> </root> </log4j>
默認初始化過程在LogManager類的靜態初始化器中完成。具體步驟如下:
轉自:http://tb.blog.csdn.net/TrackBack.aspx?PostId=1729625
1、各種索引的創建方法
(1)*tree索引
create index indexname on tablename(columnname);
(2)反向索引
create index indexname on tablename(columnname) reverse;
(3)降序索引
create index indexname on tablename(columnname DESC);
(4)位圖索引
create BITMAP index indexname on tablename(columnname);
(5)函數索引
create index indexname on tablename(functionname(columnname));
創建索引后要分析才能使用
analyze table test compute statistics for table for all indexed columns for all indexes;
analyze index test validate structure;
select name,lf_rows from index_stats;用這條語句可以查詢保存了多少條索引
analyze index test compute statistics;
從字面理解validate structure 主要在于校驗對象的有效性. compute statistics在于統計相關的信息..
查詢索引
select index_name,index_type from user_indexes where table_name='TEST';
2、打開autotrace功能
執行$ORACLE_HOME/rdbms/admin/utlxplan.sql和$ORACLE_HOME/sqlplus/admin/plustrce.sql
然后給相關用戶授予plustrace角色,然后這些用戶就可以使用autotrace功能了
3、無效索引
(1)類型不匹配
create table test(a varchar(2),b number);
insert into test values('1',1);
create index test_index on test(a);
analyze table test compute statistics for table for all indexed columns for all indexes;
set autotrace on;
類型匹配的情況
select /*+ RULE */ * from test where a='1';
A B
-- ----------
1 1
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=HINT: RULE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TEST'
2 1 INDEX (RANGE SCAN) OF 'TEST_INDEX' (NON-UNIQUE) 使用了索引
類型不匹配的情況
select /*+ RULE */ * from test where a=1;
A B
-- ----------
1 1
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=HINT: RULE
1 0 TABLE ACCESS (FULL) OF 'TEST' 選擇了全表掃描
(2)條件包含函數但沒有創建函數索引
alter system set QUERY_REWRITE_ENABLED=true;
alter system set query_rewrite_integrity=enforced;
insert into test values('a',2);
select /*+ RULE */ * from test where upper(a) = 'A';
A B
-- ----------
a 2
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=HINT: RULE
1 0 TABLE ACCESS (FULL) OF 'TEST'
由于沒有創建函數索引,所以選擇全表掃描
create index test_index_fun on test(upper(a));
analyze table test compute statistics for table for all indexed columns for all indexes;
select /*+ RULE */ * from test where upper(a) = 'A';
A B
-------------------------------------------------- ----------
a 2
a 3
a 4
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=HINT: RULE
1 0 TABLE ACCESS (FULL) OF 'TEST'
雖然創建了函數索引,但由于工作于RBO模式,所以函數索引沒用,選擇了全表掃描
select * from test where upper(a) = 'A';
A B
-------------------------------------------------- ----------
a 2
a 3
a 4
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=9)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TEST' (Cost=2 Card=1 Byt
es=9)
2 1 INDEX (RANGE SCAN) OF 'TEST_INDEX_FUN' (NON-UNIQUE) (Cos
t=1 Card=1)
當函數索引工作于CBO模式下,選擇了基于函數的索引,上面創建的索引函數TEST_INDEX_FUN已經用到
(3)符合索引中的前導列沒有被作為查詢條件
create index test_index_com on test(a,b);
select /*+ RULE */ * from test where a = '1';
A B
-- ----------
1 1
前導列a作為了查詢條件,但由于之前創建了a的索引,所以使用了TEST_INDEX而沒有使用test_index_com
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=HINT: RULE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TEST'
2 1 INDEX (RANGE SCAN) OF 'TEST_INDEX' (NON-UNIQUE)
select /*+ RULE */ * from test where b = '1';
A B
-- ----------
1 1
2 1
3 1
4 1
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=HINT: RULE
1 0 TABLE ACCESS (FULL) OF 'TEST'
前導列a沒有作為查詢條件,所以選擇全部掃描
select /*+ RULE */ * from test where b = '1' and a= '1';
A B
-- ----------
1 1
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=HINT: RULE
1 0 INDEX (RANGE SCAN) OF 'TEST_INDEX_COM' (NON-UNIQUE)
前導列a作為了查詢條件,使用了索引
(4)CBO模式下選擇的行數比例過大,優化器選擇全表掃描
declare
i number;
j number;
begin
for i in 1 .. 10 loop
for j in 1 .. 10000 loop
insert into test values(to_char(j),i);
end loop;
end loop;
end;
/
declare i number;
begin
for i in 1 .. 100 loop
insert into test values(to_char(i),i);
end loop;
end;
/
SQL> select count(*) from test;
COUNT(*)
----------
200000
select * from test where a = '1';
已選擇10000行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=27 Card=9333 Bytes=7
4664)
1 0 TABLE ACCESS (FULL) OF 'TEST' (Cost=27 Card=9333 Bytes=746
64)
比例過大,選擇全表掃描
select * from test where a = '99';
已選擇10行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=2 Bytes=16)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TEST' (Cost=2 Card=2 Byt
es=16)
2 1 INDEX (RANGE SCAN) OF 'TEST_INDEX' (NON-UNIQUE) (Cost=1
Card=2)
比例小,選擇索引
select /*+ RULE */ * from test where a = '1';
已選擇10000行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=HINT: RULE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TEST'
2 1 INDEX (RANGE SCAN) OF 'TEST_INDEX' (NON-UNIQUE)
如果指定為RBO優化器,肯定就用索引了
(5)在CBO模式下表很久沒分析,表的增長明顯,優化器采取了全表掃描
(6)索引條件中使用了<>、!=、not\not in、not like等操作符,導致查詢不使用索引
先做一個測試在一個表中插入130萬條數據,其中不等于1的數據有30萬條,以下是幾種語句執行的結果
序號 語句 時間 代價
1 select * from test where b<>1; 00: 00: 03.04 398
2 select * from test where b not like 1; 00: 00: 03.03 398
3 select * from test where b !=1; 00: 00: 03.01 398
4 select * from test where b not in(1); 00: 00: 03.00 398
5 select * from test where b<1 union select * from test where b>1; 00: 00: 03.01 264
6 select * from test where b<1 union all select * from test where b>1; 00: 00: 02.09 132
7 select * from test where b<1 or b>1; 00: 00: 02.08 96
從以上可以看出最優化的語句是7,在查詢過程中使用索引的有5、6、7
所以,如果建立了索引,在語句中盡量不要使用<>、!=、not、not in、not like操作,如果非要使用,請盡量用or和union操作替換
(7)索引對空值的影響
我們首先做一些測試數據:
SQL> create table t(x int, y int);
請注意,這里我對表t做了一個唯一(聯合)索引:
SQL> create unique index t_idx on t(x,y);
SQL> insert into t values(1,1);
SQL> insert into t values(1,NULL);
SQL> insert into t values(NULL,1);
SQL> insert into t values(NULL,NULL);
SQL> commit;
下面我們分析一下索引:
SQL> analyze index t_idx validate structure;
SQL> select name,lf_rows from index_stats;
NAME LF_ROWS
------------------------------ ----------
T_IDX 3
然后,我們就可以看到,當前的索引中僅僅保存了3行數據。
請注意,上面我們插入并提交了四行數據。
所以,這里就有一個結論:
Oracle的索引不保存該索引包含的列中全部為空的行。
這同時也帶來個好處,但當一個表中的某一列大部分為空值,至少90%以上是空值的時候,就可以為該列建立索引。
比如該表為t,該列為x
select * from t where x is null;
此時會選擇全表掃描
select * from t where x=1;
此時就會使用索引,而且索引中不保存值為空的行,所以索引中只有10%左右的行,因此在這10%的行中找出x=1的行比在全表中找出x=1的行要快的多
我們繼續插入數據,現在再插入幾行全部為空的行:
SQL> insert into t values(NULL,NULL);
SQL> insert into t values(NULL,NULL);
我們看到這樣的插入,居然沒有違反前面我們設定的唯一約束(unique on t(x,y)),
所以,這里我們又得出一個結論:
Oracle認為 NULL<>NULL ,進而 (NULL,NULL)<>(NULL,NULL)
換句話說,Oracle認為空值(NULL)不等于任何值,包括空值也不等于空值。
我們看到下面的插入會違反唯一約束(DEMO.T_IDX),這個很好理解了,因為它不是全部為空的值,即它不是(NULL,NULL),只有全部為空的行才被認為是不同的行:
SQL> insert into t values(1,null);
ORA-00001: 違反唯一約束條件 (DEMO.T_IDX)
SQL> insert into t values(null,1);
ORA-00001: 違反唯一約束條件 (DEMO.T_IDX)
SQL>
請看下面的例子:
SQL> select x,y,count(*) from t group by x,y;
X Y COUNT(*)
----- -------- ----------
3
1 1
1 1
1 1 1
Executed in 0.03 seconds
SQL> select x,y,count(*) from t where x is null and y is null group by x,y;
X Y COUNT(*)
---- ------- ----------
3
Executed in 0.01 seconds
SQL>
SQL> select x,y,count(*) from t group by x,y having count(*)>1;
X Y COUNT(*)
------ -------------------- ----------
3
Executed in 0.02 seconds
SQL>
可以看見,完全為空的行有三行,這里我們又可以得出一個結論:
oracle在group by子句中認為完全為空的行是相同的行
換句話說,在group by子句中,oracle認為(NULL,NULL)=(NULL,NULL)
SQL> select * from t where x is null;
X Y
---------- ----------
1
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=4 Bytes=8)
1 0 TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=4 Bytes=8)
SQL> select * from t where x=1;
X Y
---------- ----------
1 1
1
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=2 Bytes=4)
1 0 INDEX (RANGE SCAN) OF 'T_IDX' (UNIQUE) (Cost=1 Card=2 Byte
s=4)
從以上可以看出,在使用IS NULL 和 IS NOT NULL條件的時候,Oracle不使用索引
那么我們如何使用空值的比較條件呢?
首先,盡量不在前導列上使用空值,其次我們在創建表的時候,為每個列都指定為非空約束(NOT NULL),并且在必要的列上使用default值
8、不要為所有的列建立索引
我們知道,建立索引是為了提高查詢的效率,但是同時也應該注意到,索引增加了對DML操作(insert, update, delete)的代價,而且,一給中的索引如果太多,那么多數的索引是根本不會被使用到的,而另一方面我們維護這些不被使用的所以還要大幅度降低系統的性能。所以,索引不是越多越好,而是要恰到好處的使用。
比如說,有些列由于使用了函數,我們要使用已有的索引(如一些復合索引)是不可能的,那么就必須建立單獨的函數索引,如果說這個函數索引很少會被應用(僅僅在幾個特別的sql中會用到),我們就可以嘗試改寫查詢,而不去建立和維護那個函數索引,例如:
1,trunc函數
SQL> select empno,ename,deptno from emp where trunc(hiredate)='2004-01-01';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF 'EMP'
將上面的查詢轉換為:
SQL> select empno,ename,deptno from emp
2 where hiredate >= to_date('2004-01-01','yyyy-mm-dd')
3 and hiredate<to_date('2004-01-01','yyyy-mm-dd')+0.999;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'EMP'
2 1 INDEX (RANGE SCAN) OF 'EMP_ID3' (NON-UNIQUE)
2,to_char函數
SQL> select empno,ename,deptno from emp
2 where to_char(hiredate,'yyyy-mm-dd')='2003-09-05';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF 'EMP'
SQL> select empno,ename,deptno from emp
2 where hiredate=to_date('2003-09-05','yyyy-mm-dd');
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'EMP'
2 1 INDEX (RANGE SCAN) OF 'EMP_ID3' (NON-UNIQUE)
3,substr函數
SQL> select dname from dept where substr(dname,1,3)='abc';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF 'DEPT'
SQL> select dname from dept where dname like 'abc%';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 INDEX (RANGE SCAN) OF 'DEPT_ID1' (NON-UNIQUE)
通常,為了均衡查詢的效率和DML的效率,我們要仔細的分析應用,找出來出現頻率相對較多、字段內容較少(比如varchar2(1000)就不適合建立索引,而varchar2(10)相對來說就適合建立索引)的列,合理的建立索引,比如有時候我們希望建立復合索引,有時候我們更希望建立單鍵索引。
轉自:http://www.aygfsteel.com/hadeslee/archive/2007/11/20/161770.html
2. <body onselectstart="return false"> 取消選取、防止復制
網絡收集:PLSQL常用方法匯總
在SQLPLUS下,實現中-英字符集轉換
alter session set nls_language='AMERICAN';
alter session set nls_language='SIMPLIFIED CHINESE';
主要知識點:
一、有關表的操作
1)建表
create table test as select * from dept; --從已知表復制數據和結構
create table test as select * from dept where 1=2; --從已知表復制結構但不包括數據
2)插入數據:
insert into test select * from dept;
二、運算符
算術運算符:+ - * / 可以在select 語句中使用
連接運算符:|| select deptno|| dname from dept;
比較運算符:> >= = != < <= like between is null in
邏輯運算符:not and or
集合運算符: intersect(交),union(并 不重復), union all(并 重復), minus(差)
要求:對應集合的列數和數據類型相同
查詢中不能包含long 列
列的標簽是第一個集合的標簽
使用order by時,必須使用位置序號,不能使用列名
例:集合運算符的使用:
intersect ,union, union all, minus
select * from emp intersect select * from emp where deptno=10 ;
select * from emp minus select * from emp where deptno=10;
select * from emp where deptno=10 union select * from emp where deptno in (10,20); --不包括重復行
select * from emp where deptno=10 union all select * from emp where deptno in (10,20); --包括重復行三,常用 ORACLE 函數
sysdate為系統日期 dual為虛表
一)日期函數[重點掌握前四個日期函數]
-----------------------------------------
TO_DATE格式
Day:
dd number 12
dy abbreviated fri
day spelled out friday
ddspth spelled out, ordinal twelfth
Month:
mm number 03
mon abbreviated mar
month spelled out march
Year:
yy two digits 98
yyyy four digits 1998
Time:
HH24:MI:SS
HH12:MI:SS
24小時格式下時間范圍為: 0:00:00 - 23:59:59....
12小時格式下時間范圍為: 1:00:00 - 12:59:59 ....
-----------------------------------------
1,add_months[返回日期加(減)指定月份后(前)的日期]
select sysdate S1, add_months(sysdate, 10) S2, add_months(sysdate, 5) S3
from dual;
2,last_day [返回該月最后一天的日期]
select sysdate,last_day(sysdate) from dual;
3,months_between[返回日期之間的月份數]
select sysdate S1,
months_between('1-4月-04', sysdate) S2,
months_between('1-4月-04', '1-2月-04') S3
from dual
4,next_day(d,day): 返回下個星期的日期,day為1-7或星期日-星期六,1表示星期日
select sysdate S1, next_day(sysdate, 1) S2, next_day(sysdate, '星期日') S3
FROM DUAL
5,round[舍入到最接近的日期](day:舍入到最接近的星期日)
select sysdate S1,
round(sysdate) S2,
round(sysdate, 'year') YEAR,
round(sysdate, 'month') MONTH,
round(sysdate, 'day') DAY
from dual
6,trunc[截斷到最接近的日期]
select sysdate S1,
trunc(sysdate) S2,
trunc(sysdate, 'year') YEAR,
trunc(sysdate, 'month') MONTH,
trunc(sysdate, 'day') DAY
from dual
7,返回日期列表中最晚日期
select greatest('01-1月-04', '04-1月-04', '10-2月-04') from dual
二)字符函數(可用于字面字符或數據庫列)
1,字符串截取
select substr('abcdef',1,3) from dual
2,查找子串位置
select instr('abcfdgfdhd','fd') from dual
3,字符串連接
select 'HELLO'||'hello world' from dual;
4, 1)去掉字符串中的空格
select ltrim(' abc') s1, rtrim('zhang ') s2, trim(' zhang ') s3 from dual
2)去掉前導和后綴
select trim(leading 9 from 9998767999) s1,
trim(trailing 9 from 9998767999) s2,
trim(9 from 9998767999) s3
from dual;
5,返回字符串首字母的Ascii值
select ascii('a') from dual
6,返回ascii值對應的字母
select chr(97) from dual
7,計算字符串長度
select length('abcdef') from dual
8,initcap(首字母變大寫) ,lower(變小寫),upper(變大寫)
select lower('ABC') s1,
upper('def') s2,
initcap('efg') s3 from dual;
9,Replace
select replace('abc','b','xy') from dual;
10,translate
select translate('abc','b','xx') from dual; -- x是1位
11,lpad [左添充] rpad [右填充](用于控制輸出格式)
select lpad('func',15,'=') s1, rpad('func',15,'-') s2 from dual;
select lpad(dname,14,'=') from dept;
12, decode[實現if ..then 邏輯]
select deptno,decode(deptno,10,'1',20,'2',30,'3','其他') from dept;
三)數字函數
1,取整函數(ceil 向上取整,floor 向下取整)
select ceil(66.6) N1,floor(66.6) N2 from dual;
2, 取冪(power) 和 求平方根(sqrt)
select power(3,2) N1,sqrt(9) N2 from dual;
3,求余
select mod(9,5) from dual;
4,返回固定小數位數 (round:四舍五入,trunc:直接截斷)
select round(66.667,2) N1,trunc(66.667,2) N2 from dual;
5,返回值的符號(正數返回為1,負數為-1)
select sign(-32),sign(293) from dual;
四)轉換函數
1,to_char()[將日期和數字類型轉換成字符類型]
1) select to_char(sysdate) s1,
to_char(sysdate,'yyyy-mm-dd') s2,
to_char(sysdate,'yyyy') s3,
to_char(sysdate,'yyyy-mm-dd hh12:mi:ss') s4,
to_char(sysdate, 'hh24:mi:ss') s5,
to_char(sysdate,'DAY') s6 from dual;
2) select sal,to_char(sal,'$99999') n1,to_char(sal,'$99,999') n2 from emp
2, to_date()[將字符類型轉換為日期類型]
insert into emp(empno,hiredate) values(8000,to_date('2004-10-10','yyyy-mm-dd'));
3, to_number() 轉換為數字類型
select to_number(to_char(sysdate,'hh12')) from dual; //以數字顯示的小時數
五)其他函數
user:
返回登錄的用戶名稱
select user from dual;
vsize:
返回表達式所需的字節數
select vsize('HELLO') from dual;
nvl(ex1,ex2):
ex1值為空則返回ex2,否則返回該值本身ex1(常用)
例:如果雇員沒有傭金,將顯示0,否則顯示傭金
select comm,nvl(comm,0) from emp;
nullif(ex1,ex2):
值相等返空,否則返回第一個值
例:如果工資和傭金相等,則顯示空,否則顯示工資
select nullif(sal,comm),sal,comm from emp;
coalesce:
返回列表中第一個非空表達式
select comm,sal,coalesce(comm,sal,sal*10) from emp;
nvl2(ex1,ex2,ex3) :
如果ex1不為空,顯示ex2,否則顯示ex3
如:查看有傭金的雇員姓名以及他們的傭金
select nvl2(comm,ename,'') as HaveCommName,comm from emp;
六)分組函數
max min avg count sum
1,整個結果集是一個組
1) 求部門30 的最高工資,最低工資,平均工資,總人數,有工作的人數,工種數量及工資總和
select max(ename),max(sal),
min(ename),min(sal),
avg(sal),
count(*) ,count(job),count(distinct(job)) ,
sum(sal) from emp where deptno=30;
2, 帶group by 和 having 的分組
1)按部門分組求最高工資,最低工資,總人數,有工作的人數,工種數量及工資總和
select deptno, max(ename),max(sal),
min(ename),min(sal),
avg(sal),
count(*) ,count(job),count(distinct(job)) ,
sum(sal) from emp group by deptno;
2)部門30的最高工資,最低工資,總人數,有工作的人數,工種數量及工資總和
select deptno, max(ename),max(sal),
min(ename),min(sal),
avg(sal),
count(*) ,count(job),count(distinct(job)) ,
sum(sal) from emp group by deptno having deptno=30;
3, stddev 返回一組值的標準偏差
select deptno,stddev(sal) from emp group by deptno;
variance 返回一組值的方差差
select deptno,variance(sal) from emp group by deptno;
4, 帶有rollup和cube操作符的Group By
rollup 按分組的第一個列進行統計和最后的小計
cube 按分組的所有列的進行統計和最后的小計
select deptno,job ,sum(sal) from emp group by deptno,job;
select deptno,job ,sum(sal) from emp group by rollup(deptno,job);
cube 產生組內所有列的統計和最后的小計
select deptno,job ,sum(sal) from emp group by cube(deptno,job);七、臨時表
只在會話期間或在事務處理期間存在的表.
臨時表在插入數據時,動態分配空間
create global temporary table temp_dept
(dno number,
dname varchar2(10))
on commit delete rows;
insert into temp_dept values(10,'ABC');
commit;
select * from temp_dept; --無數據顯示,數據自動清除
on commit preserve rows:在會話期間表一直可以存在(保留數據)
on commit delete rows:事務結束清除數據(在事務結束時自動刪除表的數據)
Oracle時間日期操作
sysdate+(5/24/60/60) 在系統時間基礎上延遲5秒
sysdate+5/24/60 在系統時間基礎上延遲5分鐘
sysdate+5/24 在系統時間基礎上延遲5小時
sysdate+5 在系統時間基礎上延遲5天
add_months(sysdate,-5) 在系統時間基礎上延遲5月
add_months(sysdate,-5*12) 在系統時間基礎上延遲5年
上月末的日期:select last_day(add_months(sysdate, -1)) from dual;
本月的最后一秒:select trunc(add_months(sysdate,1),'MM') - 1/24/60/60 from dual
本周星期一的日期:select trunc(sysdate,'day')+1 from dual
年初至今的天數:select ceil(sysdate - trunc(sysdate, 'year')) from dual;
今天是今年的第幾周 :select to_char(sysdate,'fmww') from dual
今天是本月的第幾周:SELECT TO_CHAR(SYSDATE,'WW') - TO_CHAR(TRUNC(SYSDATE,'MM'),'WW') + 1 AS "weekOfMon" FROM dual
本月的天數
SELECT to_char(last_day(SYSDATE),'dd') days FROM dual
今年的天數
select add_months(trunc(sysdate,'year'), 12) - trunc(sysdate,'year') from dual
下個星期一的日期
SELECT Next_day(SYSDATE,'monday') FROM dual
============================================
--計算工作日方法
create table t(s date,e date);
alter session set nls_date_format = 'yyyy-mm-dd';
insert into t values('2003-03-01','2003-03-03');
insert into t values('2003-03-02','2003-03-03');
insert into t values('2003-03-07','2003-03-08');
insert into t values('2003-03-07','2003-03-09');
insert into t values('2003-03-05','2003-03-07');
insert into t values('2003-02-01','2003-03-31');
-- 這里假定日期都是不帶時間的,否則在所有日期前加trunc即可。
select s,e,e-s+1 total_days,
trunc((e-s+1)/7)*5 + length(replace(substr('01111100111110',to_char(s,'d'),mod(e-s+1,7)),'0','')) work_days
from t;
-- drop table t;
引此:http://www.itpub.net/showthread.php?s=1635506cd5f48b1bc3adbe4cde96f227&threadid=104060&perpage=15&pagenumber=1
================================================================================
判斷當前時間是上午下午還是晚上
SELECT CASE
WHEN to_number(to_char(SYSDATE,'hh24')) BETWEEN 6 AND 11 THEN '上午'
WHEN to_number(to_char(SYSDATE,'hh24')) BETWEEN 11 AND 17 THEN '下午'
WHEN to_number(to_char(SYSDATE,'hh24')) BETWEEN 17 AND 21 THEN '晚上'
END
FROM dual;
================================================================================
Oracle 中的一些處理日期
將數字轉換為任意時間格式.如秒:需要轉換為天/小時
SELECT to_char(floor(TRUNC(936000/(60*60))/24))||'天'||to_char(mod(TRUNC(936000/(60*60)),24))||'小時' FROM DUAL
TO_DATE格式
Day:
dd number 12
dy abbreviated fri
day spelled out friday
ddspth spelled out, ordinal twelfth
Month:
mm number 03
mon abbreviated mar
month spelled out march
Year:
yy two digits 98
yyyy four digits 1998
24小時格式下時間范圍為: 0:00:00 - 23:59:59....
12小時格式下時間范圍為: 1:00:00 - 12:59:59 ....
1.
日期和字符轉換函數用法(to_date,to_char)
2.
select to_char( to_date(222,'J'),'Jsp') from dual
顯示Two Hundred Twenty-Two
3.
求某天是星期幾
select to_char(to_date('2002-08-26','yyyy-mm-dd'),'day') from dual;
星期一
select to_char(to_date('2002-08-26','yyyy-mm-dd'),'day','NLS_DATE_LANGUAGE = American') from dual;
monday
設置日期語言
ALTER SESSION SET NLS_DATE_LANGUAGE='AMERICAN';
也可以這樣
TO_DATE ('2002-08-26', 'YYYY-mm-dd', 'NLS_DATE_LANGUAGE = American')
4.
兩個日期間的天數
select floor(sysdate - to_date('20020405','yyyymmdd')) from dual;
5. 時間為null的用法
select id, active_date from table1
UNION
select 1, TO_DATE(null) from dual;
注意要用TO_DATE(null)
6.
a_date between to_date('20011201','yyyymmdd') and to_date('20011231','yyyymmdd')
那么12月31號中午12點之后和12月1號的12點之前是不包含在這個范圍之內的。
所以,當時間需要精確的時候,覺得to_char還是必要的
7. 日期格式沖突問題
輸入的格式要看你安裝的ORACLE字符集的類型, 比如: US7ASCII, date格式的類型就是: '01-Jan-01'
alter system set NLS_DATE_LANGUAGE = American
alter session set NLS_DATE_LANGUAGE = American
或者在to_date中寫
select to_char(to_date('2002-08-26','yyyy-mm-dd'),'day','NLS_DATE_LANGUAGE = American') from dual;
注意我這只是舉了NLS_DATE_LANGUAGE,當然還有很多,
可查看
select * from nls_session_parameters
select * from V$NLS_PARAMETERS
8.
select count(*)
from ( select rownum-1 rnum
from all_objects
where rownum <= to_date('2002-02-28','yyyy-mm-dd') - to_date('2002-
02-01','yyyy-mm-dd')+1
)
where to_char( to_date('2002-02-01','yyyy-mm-dd')+rnum-1, 'D' )
not
in ( '1', '7' )
查找2002-02-28至2002-02-01間除星期一和七的天數
在前后分別調用DBMS_UTILITY.GET_TIME, 讓后將結果相減(得到的是1/100秒, 而不是毫秒).
9.
select months_between(to_date('01-31-1999','MM-DD-YYYY'),
to_date('12-31-1998','MM-DD-YYYY')) "MONTHS" FROM DUAL;
1
select months_between(to_date('02-01-1999','MM-DD-YYYY'),
to_date('12-31-1998','MM-DD-YYYY')) "MONTHS" FROM DUAL;
1.03225806451613
10. Next_day的用法
Next_day(date, day)
Monday-Sunday, for format code DAY
Mon-Sun, for format code DY
1-7, for format code D
11
select to_char(sysdate,'hh:mi:ss') TIME from all_objects
注意:第一條記錄的TIME 與最后一行是一樣的
可以建立一個函數來處理這個問題
create or replace function sys_date return date is
begin
return sysdate;
end;
select to_char(sys_date,'hh:mi:ss') from all_objects;
12.
獲得小時數
SELECT EXTRACT(HOUR FROM TIMESTAMP '2001-02-16 2:38:40') from offer
SQL> select sysdate ,to_char(sysdate,'hh') from dual;
SYSDATE TO_CHAR(SYSDATE,'HH')
-------------------- ---------------------
2003-10-13 19:35:21 07
SQL> select sysdate ,to_char(sysdate,'hh24') from dual;
SYSDATE TO_CHAR(SYSDATE,'HH24')
-------------------- -----------------------
2003-10-13 19:35:21 19
獲取年月日與此類似
13.
年月日的處理
select older_date,
newer_date,
years,
months,
abs(
trunc(
newer_date-
add_months( older_date,years*12+months )
)
) days
from ( select
trunc(months_between( newer_date, older_date )/12) YEARS,
mod(trunc(months_between( newer_date, older_date )),
12 ) MONTHS,
newer_date,
older_date
from ( select hiredate older_date,
add_months(hiredate,rownum)+rownum newer_date
from emp )
)
14.
處理月份天數不定的辦法
select to_char(add_months(last_day(sysdate) +1, -2), 'yyyymmdd'),last_day(sysdate) from dual
16.
找出今年的天數
select add_months(trunc(sysdate,'year'), 12) - trunc(sysdate,'year') from dual
閏年的處理方法
to_char( last_day( to_date('02' | | :year,'mmyyyy') ), 'dd' )
如果是28就不是閏年
17.
yyyy與rrrr的區別
'YYYY99 TO_C
------- ----
yyyy 99 0099
rrrr 99 1999
yyyy 01 0001
rrrr 01 2001
18.不同時區的處理
select to_char( NEW_TIME( sysdate, 'GMT','EST'), 'dd/mm/yyyy hh:mi:ss') ,sysdate
from dual;
19.
5秒鐘一個間隔
Select TO_DATE(FLOOR(TO_CHAR(sysdate,'SSSSS')/300) * 300,'SSSSS') ,TO_CHAR(sysdate,'SSSSS')
from dual
2002-11-1 9:55:00 35786
SSSSS表示5位秒數
20.
一年的第幾天
select TO_CHAR(SYSDATE,'DDD'),sysdate from dual
310 2002-11-6 10:03:51
21.計算小時,分,秒,毫秒
select
Days,
A,
TRUNC(A*24) Hours,
TRUNC(A*24*60 - 60*TRUNC(A*24)) Minutes,
TRUNC(A*24*60*60 - 60*TRUNC(A*24*60)) Seconds,
TRUNC(A*24*60*60*100 - 100*TRUNC(A*24*60*60)) mSeconds
from
(
select
trunc(sysdate) Days,
sysdate - trunc(sysdate) A
from dual
)
select * from tabname
order by decode(mode,'FIFO',1,-1)*to_char(rq,'yyyymmddhh24miss');
//
floor((date2-date1) /365) 作為年
floor((date2-date1, 365) /30) 作為月
mod(mod(date2-date1, 365), 30)作為日.
23.next_day函數
next_day(sysdate,6)是從當前開始下一個星期五。后面的數字是從星期日開始算起。
1 2 3 4 5 6 7
日 一 二 三 四 五 六
---------------------------------------------------------------
select (sysdate-to_date('2003-12-03 12:55:45','yyyy-mm-dd hh24:mi:ss'))*24*60*60 from dual
日期 返回的是天 然后 轉換為ss
轉此:http://www.onlinedatabase.cn/leadbbs/Announce/Announce.asp?BoardID=42&ID=1769
將數字轉換為任意時間格式.如秒:需要轉換為天/小時
SELECT to_char(floor(TRUNC(936000/(60*60))/24))||'天'||to_char(mod(TRUNC(936000/(60*60)),24))||'小時' FROM DUAL
TO_DATE格式
Day:
dd number 12
dy abbreviated fri
day spelled out friday
ddspth spelled out, ordinal twelfth
Month:
mm number 03
mon abbreviated mar
month spelled out march
Year:
yy two digits 98
yyyy four digits 1998
24小時格式下時間范圍為: 0:00:00 - 23:59:59....
12小時格式下時間范圍為: 1:00:00 - 12:59:59 ....
1.
日期和字符轉換函數用法(to_date,to_char)
2.
select to_char( to_date(222,'J'),'Jsp') from dual
顯示Two Hundred Twenty-Two
3.
求某天是星期幾
select to_char(to_date('2002-08-26','yyyy-mm-dd'),'day') from dual;
星期一
select to_char(to_date('2002-08-26','yyyy-mm-dd'),'day','NLS_DATE_LANGUAGE = American') from dual;
monday
設置日期語言
ALTER SESSION SET NLS_DATE_LANGUAGE='AMERICAN';
也可以這樣
TO_DATE ('2002-08-26', 'YYYY-mm-dd', 'NLS_DATE_LANGUAGE = American')
4.
兩個日期間的天數
select floor(sysdate - to_date('20020405','yyyymmdd')) from dual;
5. 時間為null的用法
select id, active_date from table1
UNION
select 1, TO_DATE(null) from dual;
注意要用TO_DATE(null)
6.
a_date between to_date('20011201','yyyymmdd') and to_date('20011231','yyyymmdd')
那么12月31號中午12點之后和12月1號的12點之前是不包含在這個范圍之內的。
所以,當時間需要精確的時候,覺得to_char還是必要的
7. 日期格式沖突問題
輸入的格式要看你安裝的ORACLE字符集的類型, 比如: US7ASCII, date格式的類型就是: '01-Jan-01'
alter system set NLS_DATE_LANGUAGE = American
alter session set NLS_DATE_LANGUAGE = American
或者在to_date中寫
select to_char(to_date('2002-08-26','yyyy-mm-dd'),'day','NLS_DATE_LANGUAGE = American') from dual;
注意我這只是舉了NLS_DATE_LANGUAGE,當然還有很多,
可查看
select * from nls_session_parameters
select * from V$NLS_PARAMETERS
8.
select count(*)
from ( select rownum-1 rnum
from all_objects
where rownum <= to_date('2002-02-28','yyyy-mm-dd') - to_date('2002-
02-01','yyyy-mm-dd')+1
)
where to_char( to_date('2002-02-01','yyyy-mm-dd')+rnum-1, 'D' )
not
in ( '1', '7' )
查找2002-02-28至2002-02-01間除星期一和七的天數
在前后分別調用DBMS_UTILITY.GET_TIME, 讓后將結果相減(得到的是1/100秒, 而不是毫秒).
9.
select months_between(to_date('01-31-1999','MM-DD-YYYY'),
to_date('12-31-1998','MM-DD-YYYY')) "MONTHS" FROM DUAL;
1
select months_between(to_date('02-01-1999','MM-DD-YYYY'),
to_date('12-31-1998','MM-DD-YYYY')) "MONTHS" FROM DUAL;
1.03225806451613
10. Next_day的用法
Next_day(date, day)
Monday-Sunday, for format code DAY
Mon-Sun, for format code DY
1-7, for format code D
11
select to_char(sysdate,'hh:mi:ss') TIME from all_objects
注意:第一條記錄的TIME 與最后一行是一樣的
可以建立一個函數來處理這個問題
create or replace function sys_date return date is
begin
return sysdate;
end;
select to_char(sys_date,'hh:mi:ss') from all_objects;
12.
獲得小時數
SELECT EXTRACT(HOUR FROM TIMESTAMP '2001-02-16 2:38:40') from offer
SQL> select sysdate ,to_char(sysdate,'hh') from dual;
SYSDATE TO_CHAR(SYSDATE,'HH')
-------------------- ---------------------
2003-10-13 19:35:21 07
SQL> select sysdate ,to_char(sysdate,'hh24') from dual;
SYSDATE TO_CHAR(SYSDATE,'HH24')
-------------------- -----------------------
2003-10-13 19:35:21 19
獲取年月日與此類似
13.
年月日的處理
select older_date,
newer_date,
years,
months,
abs(
trunc(
newer_date-
add_months( older_date,years*12+months )
)
) days
from ( select
trunc(months_between( newer_date, older_date )/12) YEARS,
mod(trunc(months_between( newer_date, older_date )),
12 ) MONTHS,
newer_date,
older_date
from ( select hiredate older_date,
add_months(hiredate,rownum)+rownum newer_date
from emp )
)
14.
處理月份天數不定的辦法
select to_char(add_months(last_day(sysdate) +1, -2), 'yyyymmdd'),last_day(sysdate) from dual
16.
找出今年的天數
select add_months(trunc(sysdate,'year'), 12) - trunc(sysdate,'year') from dual
閏年的處理方法
to_char( last_day( to_date('02' | | :year,'mmyyyy') ), 'dd' )
如果是28就不是閏年
17.
yyyy與rrrr的區別
'YYYY99 TO_C
------- ----
yyyy 99 0099
rrrr 99 1999
yyyy 01 0001
rrrr 01 2001
18.不同時區的處理
select to_char( NEW_TIME( sysdate, 'GMT','EST'), 'dd/mm/yyyy hh:mi:ss') ,sysdate
from dual;
19.
5秒鐘一個間隔
Select TO_DATE(FLOOR(TO_CHAR(sysdate,'SSSSS')/300) * 300,'SSSSS') ,TO_CHAR(sysdate,'SSSSS')
from dual
2002-11-1 9:55:00 35786
SSSSS表示5位秒數
20.
一年的第幾天
select TO_CHAR(SYSDATE,'DDD'),sysdate from dual
310 2002-11-6 10:03:51
21.計算小時,分,秒,毫秒
select
Days,
A,
TRUNC(A*24) Hours,
TRUNC(A*24*60 - 60*TRUNC(A*24)) Minutes,
TRUNC(A*24*60*60 - 60*TRUNC(A*24*60)) Seconds,
TRUNC(A*24*60*60*100 - 100*TRUNC(A*24*60*60)) mSeconds
from
(
select
trunc(sysdate) Days,
sysdate - trunc(sysdate) A
from dual
)
select * from tabname
order by decode(mode,'FIFO',1,-1)*to_char(rq,'yyyymmddhh24miss');
//
floor((date2-date1) /365) 作為年
floor((date2-date1, 365) /30) 作為月
mod(mod(date2-date1, 365), 30)作為日.
23.next_day函數
next_day(sysdate,6)是從當前開始下一個星期五。后面的數字是從星期日開始算起。
1 2 3 4 5 6 7
日 一 二 三 四 五 六
---------------------------------------------------------------
select (sysdate-to_date('2003-12-03 12:55:45','yyyy-mm-dd hh24:mi:ss'))*24*60*60 from dual
日期 返回的是天 然后 轉換為ss
轉此:http://www.onlinedatabase.cn/leadbbs/Announce/Announce.asp?BoardID=42&ID=1769
將數字轉換為任意時間格式.如秒:需要轉換為天/小時
SELECT to_char(floor(TRUNC(936000/(60*60))/24))||'天'||to_char(mod(TRUNC(936000/(60*60)),24))||'小時' FROM DUAL
TO_DATE格式
Day:
dd number 12
dy abbreviated fri
day spelled out friday
ddspth spelled out, ordinal twelfth
Month:
mm number 03
mon abbreviated mar
month spelled out march
Year:
yy two digits 98
yyyy four digits 1998
24小時格式下時間范圍為: 0:00:00 - 23:59:59....
12小時格式下時間范圍為: 1:00:00 - 12:59:59 ....
1.
日期和字符轉換函數用法(to_date,to_char)
2.
select to_char( to_date(222,'J'),'Jsp') from dual
顯示Two Hundred Twenty-Two
3.
求某天是星期幾
select to_char(to_date('2002-08-26','yyyy-mm-dd'),'day') from dual;
星期一
select to_char(to_date('2002-08-26','yyyy-mm-dd'),'day','NLS_DATE_LANGUAGE = American') from dual;
monday
設置日期語言
ALTER SESSION SET NLS_DATE_LANGUAGE='AMERICAN';
也可以這樣
TO_DATE ('2002-08-26', 'YYYY-mm-dd', 'NLS_DATE_LANGUAGE = American')
4.
兩個日期間的天數
select floor(sysdate - to_date('20020405','yyyymmdd')) from dual;
5. 時間為null的用法
select id, active_date from table1
UNION
select 1, TO_DATE(null) from dual;
注意要用TO_DATE(null)
6.
a_date between to_date('20011201','yyyymmdd') and to_date('20011231','yyyymmdd')
那么12月31號中午12點之后和12月1號的12點之前是不包含在這個范圍之內的。
所以,當時間需要精確的時候,覺得to_char還是必要的
7. 日期格式沖突問題
輸入的格式要看你安裝的ORACLE字符集的類型, 比如: US7ASCII, date格式的類型就是: '01-Jan-01'
alter system set NLS_DATE_LANGUAGE = American
alter session set NLS_DATE_LANGUAGE = American
或者在to_date中寫
select to_char(to_date('2002-08-26','yyyy-mm-dd'),'day','NLS_DATE_LANGUAGE = American') from dual;
注意我這只是舉了NLS_DATE_LANGUAGE,當然還有很多,
可查看
select * from nls_session_parameters
select * from V$NLS_PARAMETERS
8.
select count(*)
from ( select rownum-1 rnum
from all_objects
where rownum <= to_date('2002-02-28','yyyy-mm-dd') - to_date('2002-
02-01','yyyy-mm-dd')+1
)
where to_char( to_date('2002-02-01','yyyy-mm-dd')+rnum-1, 'D' )
not
in ( '1', '7' )
查找2002-02-28至2002-02-01間除星期一和七的天數
在前后分別調用DBMS_UTILITY.GET_TIME, 讓后將結果相減(得到的是1/100秒, 而不是毫秒).
9.
select months_between(to_date('01-31-1999','MM-DD-YYYY'),
to_date('12-31-1998','MM-DD-YYYY')) "MONTHS" FROM DUAL;
1
select months_between(to_date('02-01-1999','MM-DD-YYYY'),
to_date('12-31-1998','MM-DD-YYYY')) "MONTHS" FROM DUAL;
1.03225806451613
10. Next_day的用法
Next_day(date, day)
Monday-Sunday, for format code DAY
Mon-Sun, for format code DY
1-7, for format code D
11
select to_char(sysdate,'hh:mi:ss') TIME from all_objects
注意:第一條記錄的TIME 與最后一行是一樣的
可以建立一個函數來處理這個問題
create or replace function sys_date return date is
begin
return sysdate;
end;
select to_char(sys_date,'hh:mi:ss') from all_objects;
12.
獲得小時數
SELECT EXTRACT(HOUR FROM TIMESTAMP '2001-02-16 2:38:40') from offer
SQL> select sysdate ,to_char(sysdate,'hh') from dual;
SYSDATE TO_CHAR(SYSDATE,'HH')
-------------------- ---------------------
2003-10-13 19:35:21 07
SQL> select sysdate ,to_char(sysdate,'hh24') from dual;
SYSDATE TO_CHAR(SYSDATE,'HH24')
-------------------- -----------------------
2003-10-13 19:35:21 19
獲取年月日與此類似
13.
年月日的處理
select older_date,
newer_date,
years,
months,
abs(
trunc(
newer_date-
add_months( older_date,years*12+months )
)
) days
from ( select
trunc(months_between( newer_date, older_date )/12) YEARS,
mod(trunc(months_between( newer_date, older_date )),
12 ) MONTHS,
newer_date,
older_date
from ( select hiredate older_date,
add_months(hiredate,rownum)+rownum newer_date
from emp )
)
14.
處理月份天數不定的辦法
select to_char(add_months(last_day(sysdate) +1, -2), 'yyyymmdd'),last_day(sysdate) from dual
16.
找出今年的天數
select add_months(trunc(sysdate,'year'), 12) - trunc(sysdate,'year') from dual
閏年的處理方法
to_char( last_day( to_date('02' | | :year,'mmyyyy') ), 'dd' )
如果是28就不是閏年
17.
yyyy與rrrr的區別
'YYYY99 TO_C
------- ----
yyyy 99 0099
rrrr 99 1999
yyyy 01 0001
rrrr 01 2001
18.不同時區的處理
select to_char( NEW_TIME( sysdate, 'GMT','EST'), 'dd/mm/yyyy hh:mi:ss') ,sysdate
from dual;
19.
5秒鐘一個間隔
Select TO_DATE(FLOOR(TO_CHAR(sysdate,'SSSSS')/300) * 300,'SSSSS') ,TO_CHAR(sysdate,'SSSSS')
from dual
2002-11-1 9:55:00 35786
SSSSS表示5位秒數
20.
一年的第幾天
select TO_CHAR(SYSDATE,'DDD'),sysdate from dual
310 2002-11-6 10:03:51
21.計算小時,分,秒,毫秒
select
Days,
A,
TRUNC(A*24) Hours,
TRUNC(A*24*60 - 60*TRUNC(A*24)) Minutes,
TRUNC(A*24*60*60 - 60*TRUNC(A*24*60)) Seconds,
TRUNC(A*24*60*60*100 - 100*TRUNC(A*24*60*60)) mSeconds
from
(
select
trunc(sysdate) Days,
sysdate - trunc(sysdate) A
from dual
)
select * from tabname
order by decode(mode,'FIFO',1,-1)*to_char(rq,'yyyymmddhh24miss');
//
floor((date2-date1) /365) 作為年
floor((date2-date1, 365) /30) 作為月
mod(mod(date2-date1, 365), 30)作為日.
23.next_day函數
next_day(sysdate,6)是從當前開始下一個星期五。后面的數字是從星期日開始算起。
1 2 3 4 5 6 7
日 一 二 三 四 五 六
---------------------------------------------------------------
select (sysdate-to_date('2003-12-03 12:55:45','yyyy-mm-dd hh24:mi:ss'))*24*60*60 from dual
日期 返回的是天 然后 轉換為ss
轉此:http://www.onlinedatabase.cn/leadbbs/Announce/Announce.asp?BoardID=42&ID=1769
將數字轉換為任意時間格式.如秒:需要轉換為天/小時
SELECT to_char(floor(TRUNC(936000/(60*60))/24))||'天'||to_char(mod(TRUNC(936000/(60*60)),24))||'小時' FROM DUAL
TO_DATE格式
Day:
dd number 12
dy abbreviated fri
day spelled out friday
ddspth spelled out, ordinal twelfth
Month:
mm number 03
mon abbreviated mar
month spelled out march
Year:
yy two digits 98
yyyy four digits 1998
24小時格式下時間范圍為: 0:00:00 - 23:59:59....
12小時格式下時間范圍為: 1:00:00 - 12:59:59 ....
1.
日期和字符轉換函數用法(to_date,to_char)
2.
select to_char( to_date(222,'J'),'Jsp') from dual
顯示Two Hundred Twenty-Two
3.
求某天是星期幾
select to_char(to_date('2002-08-26','yyyy-mm-dd'),'day') from dual;
星期一
select to_char(to_date('2002-08-26','yyyy-mm-dd'),'day','NLS_DATE_LANGUAGE = American') from dual;
monday
設置日期語言
ALTER SESSION SET NLS_DATE_LANGUAGE='AMERICAN';
也可以這樣
TO_DATE ('2002-08-26', 'YYYY-mm-dd', 'NLS_DATE_LANGUAGE = American')
4.
兩個日期間的天數
select floor(sysdate - to_date('20020405','yyyymmdd')) from dual;
5. 時間為null的用法
select id, active_date from table1
UNION
select 1, TO_DATE(null) from dual;
注意要用TO_DATE(null)
6.
a_date between to_date('20011201','yyyymmdd') and to_date('20011231','yyyymmdd')
那么12月31號中午12點之后和12月1號的12點之前是不包含在這個范圍之內的。
所以,當時間需要精確的時候,覺得to_char還是必要的
7. 日期格式沖突問題
輸入的格式要看你安裝的ORACLE字符集的類型, 比如: US7ASCII, date格式的類型就是: '01-Jan-01'
alter system set NLS_DATE_LANGUAGE = American
alter session set NLS_DATE_LANGUAGE = American
或者在to_date中寫
select to_char(to_date('2002-08-26','yyyy-mm-dd'),'day','NLS_DATE_LANGUAGE = American') from dual;
注意我這只是舉了NLS_DATE_LANGUAGE,當然還有很多,
可查看
select * from nls_session_parameters
select * from V$NLS_PARAMETERS
8.
select count(*)
from ( select rownum-1 rnum
from all_objects
where rownum <= to_date('2002-02-28','yyyy-mm-dd') - to_date('2002-
02-01','yyyy-mm-dd')+1
)
where to_char( to_date('2002-02-01','yyyy-mm-dd')+rnum-1, 'D' )
not
in ( '1', '7' )
查找2002-02-28至2002-02-01間除星期一和七的天數
在前后分別調用DBMS_UTILITY.GET_TIME, 讓后將結果相減(得到的是1/100秒, 而不是毫秒).
9.
select months_between(to_date('01-31-1999','MM-DD-YYYY'),
to_date('12-31-1998','MM-DD-YYYY')) "MONTHS" FROM DUAL;
1
select months_between(to_date('02-01-1999','MM-DD-YYYY'),
to_date('12-31-1998','MM-DD-YYYY')) "MONTHS" FROM DUAL;
1.03225806451613
10. Next_day的用法
Next_day(date, day)
Monday-Sunday, for format code DAY
Mon-Sun, for format code DY
1-7, for format code D
11
select to_char(sysdate,'hh:mi:ss') TIME from all_objects
注意:第一條記錄的TIME 與最后一行是一樣的
可以建立一個函數來處理這個問題
create or replace function sys_date return date is
begin
return sysdate;
end;
select to_char(sys_date,'hh:mi:ss') from all_objects;
12.
獲得小時數
SELECT EXTRACT(HOUR FROM TIMESTAMP '2001-02-16 2:38:40') from offer
SQL> select sysdate ,to_char(sysdate,'hh') from dual;
SYSDATE TO_CHAR(SYSDATE,'HH')
-------------------- ---------------------
2003-10-13 19:35:21 07
SQL> select sysdate ,to_char(sysdate,'hh24') from dual;
SYSDATE TO_CHAR(SYSDATE,'HH24')
-------------------- -----------------------
2003-10-13 19:35:21 19
獲取年月日與此類似
13.
年月日的處理
select older_date,
newer_date,
years,
months,
abs(
trunc(
newer_date-
add_months( older_date,years*12+months )
)
) days
from ( select
trunc(months_between( newer_date, older_date )/12) YEARS,
mod(trunc(months_between( newer_date, older_date )),
12 ) MONTHS,
newer_date,
older_date
from ( select hiredate older_date,
add_months(hiredate,rownum)+rownum newer_date
from emp )
)
14.
處理月份天數不定的辦法
select to_char(add_months(last_day(sysdate) +1, -2), 'yyyymmdd'),last_day(sysdate) from dual
16.
找出今年的天數
select add_months(trunc(sysdate,'year'), 12) - trunc(sysdate,'year') from dual
閏年的處理方法
to_char( last_day( to_date('02' | | :year,'mmyyyy') ), 'dd' )
如果是28就不是閏年
17.
yyyy與rrrr的區別
'YYYY99 TO_C
------- ----
yyyy 99 0099
rrrr 99 1999
yyyy 01 0001
rrrr 01 2001
18.不同時區的處理
select to_char( NEW_TIME( sysdate, 'GMT','EST'), 'dd/mm/yyyy hh:mi:ss') ,sysdate
from dual;
19.
5秒鐘一個間隔
Select TO_DATE(FLOOR(TO_CHAR(sysdate,'SSSSS')/300) * 300,'SSSSS') ,TO_CHAR(sysdate,'SSSSS')
from dual
2002-11-1 9:55:00 35786
SSSSS表示5位秒數
20.
一年的第幾天
select TO_CHAR(SYSDATE,'DDD'),sysdate from dual
310 2002-11-6 10:03:51
21.計算小時,分,秒,毫秒
select
Days,
A,
TRUNC(A*24) Hours,
TRUNC(A*24*60 - 60*TRUNC(A*24)) Minutes,
TRUNC(A*24*60*60 - 60*TRUNC(A*24*60)) Seconds,
TRUNC(A*24*60*60*100 - 100*TRUNC(A*24*60*60)) mSeconds
from
(
select
trunc(sysdate) Days,
sysdate - trunc(sysdate) A
from dual
)
select * from tabname
order by decode(mode,'FIFO',1,-1)*to_char(rq,'yyyymmddhh24miss');
//
floor((date2-date1) /365) 作為年
floor((date2-date1, 365) /30) 作為月
mod(mod(date2-date1, 365), 30)作為日.
23.next_day函數
next_day(sysdate,6)是從當前開始下一個星期五。后面的數字是從星期日開始算起。
1 2 3 4 5 6 7
日 一 二 三 四 五 六
---------------------------------------------------------------
select (sysdate-to_date('2003-12-03 12:55:45','yyyy-mm-dd hh24:mi:ss'))*24*60*60 from dual
日期 返回的是天 然后 轉換為ss
import java.util.Calendar;