2009年12月21日 星期一

SQL 日期的應用 (轉)

  oracle SQL裡常用的時間函數,經典推薦
相信很多人都有過統計某些數據的經歷,比如,要統計財務的情況,可能要按每年,每季度,每月,甚至每個星期來分別統計。 那在oracle中應該怎麼來寫sql語句呢,這個時候Oracle的日期函數會給我們很多幫助。
常用日期型函數
1。  Sysdate當前日期和時間
 SQL> Select sysdate from dual;
SYSDATE
----------
 21-6月-05
2。  Last_day本月最後一天
 SQL> Select last_day(sysdate) from dual;
LAST_DAY(S
----------
 30-6月-05
3。  Add_months(d,n)當前日期d後推n個月用於從一個日期值增加或減少一些月份
date_value:=add_months(date_value,number_of_months)
 SQL> Select add_months(sysdate,2) from dual;
ADD_MONTHS
----------
 21-8月-05
4。  Months_between(f,s)日期f和s間相差月數
 SQL> select months_between(sysdate,to_date('2005-11-12','yyyy-mm-dd'))from dual;
MONTHS_BETWEEN(SYSDATE,TO_DATE('2005-11-12','YYYY-MM-DD'))
 -------------------------------------------------- --------
-4.6966741
5。  NEXT_DAY(d, day_of_week)
返回由"day_of_week"命名的,在變量"d"指定的日期之後的第一個工作日的日期。 參數"day_of_week"必須為該星期中的某一天。
 SQL> SELECT next_day(to_date('20050620','YYYYMMDD'),1) FROM dual;
NEXT_DAY(T
----------
 26-6月-05
6。  current_date()返回當前會話時區中的當前日期
date_value:=current_date
 SQL> column sessiontimezone for a15
 SQL> select sessiontimezone,current_date from dual;
 SESSIONTIMEZONE CURRENT_DA
 --------------- ----------
 +08:00 13-11月-03

 SQL> alter session set time_zone='-11:00' 2 /
會話已更改。
 SQL> select sessiontimezone,current_timestamp from dual;
 SESSIONTIMEZONE CURRENT_TIMESTAMP
 --------------- ----------------------------------- -
 -11:00 12-11月-03 04.59.13.668000下午-11:00
7。  current_timestamp()以timestamp with time zone數據類型返回當前會話時區中的當前日期
 SQL> select current_timestamp from dual;
CURRENT_TIMESTAMP
 -------------------------------------------------- -------------------------
 21-6月-05 10.13.08.220589上午+08:00
8。  dbtimezone()返回時區
 SQL> select dbtimezone from dual;
DBTIME
------
-08:00
9。  extract()找出日期或間隔值的字段值
 date_value:=extract(date_field from [datetime_value|interval_value])
 SQL> select extract(month from sysdate) "This Month" from dual;
 This Month
----------
6
 SQL> select extract(year from add_months(sysdate,36)) " Years" from dual;
Years
----------
2008
10。  localtimestamp()返回會話中的日期和時間
 SQL> select localtimestamp from dual;
LOCALTIMESTAMP
 -------------------------------------------------- -------------------------
 21-6月-05 10.18.15.855652上午
11。取得week number
TO_CHAR(TO_DATE('2007/05/16','YYYY/MM/DD'),'IW') AS WEEK




常用日期數據格式(該段為摘抄)
 Y或YY或YYY年的最後一位,兩位或三位Select to_char(sysdate,’YYY’) from dual; 002表示2002年
 SYEAR或YEAR SYEAR使公元前的年份前加一負號Select to_char(sysdate,’SYEAR’) from dual; -1112表示公元前111 2年
 Q季度,1~3月為第一季度Select to_char(sysdate,’Q’) from dual; 2表示第二季度①
 MM月份數Select to_char(sysdate,’MM’) from dual; 12表示12月
 RM月份的羅馬錶示Select to_char(sysdate,’RM’) from dual; IV表示4月
 Month用9個字符長度表示的月份名Select to_char(sysdate,’Month’) from dual; May後跟6個空格表示5月
 WW當年第幾週Select to_char(sysdate,’WW’) from dual; 24表示2002年6月13日為第24週
 W本月第幾週Select to_char(sysdate,’W’) from dual; 2002年10月1日為第1週
 DDD當年第幾, 1月1日為001,2月1日為032 Select to_char(sysdate,’DDD’) from dual; 363 2002年1 2月2 9日為第363天
 DD當月第幾天Select to_char(sysdate,’DD’) from dual; 04 10月4日為第4天
 D週內第幾天Select to_char(sysdate,’D’) from dual; 5 2002年3月14日為星期一
 DY週內第幾天縮寫Select to_char(sysdate,’DY’) from dual; SUN 2002年3月24日為星期天
 HH或HH12 12進制小時數Select to_char(sysdate,’HH’) from dual; 02午夜2點過8分為02
 HH24 24小時制Select to_char(sysdate,’HH24’) from dual; 14下午2點08分為14
 MI分鐘數(0~59) Select to_char(sysdate,’MI’) from dual; 17下午4點17分
 SS秒數(0~59) Select to_char(sysdate,’SS’) from dual; 22 11點3分22秒提示注意不要將MM格式用於分鐘(分鐘應該使用MI)。  MM是用於月份的格式,將它用於分鐘也能工作,但結果是錯誤的。

現在給出一些實踐後的用法:
1。 上月末天:
 SQL> select to_char(add_months(last_day(sysdate),-1),'yyyy-MM-dd') LastDay from dual;
LASTDAY
----------
2005-05-31
2。 上月今天
 SQL> select to_char(add_months(sysdate,-1),'yyyy-MM-dd') PreToday from dual;

PRETODAY
----------
2005-05-21
 3.上月首天
 SQL> select to_char(add_months(last_day(sysdate)+1,-2),'yyyy-MM-dd') firstDay from dual;
FIRSTDAY
----------
2005-05-01
 4.按照每週進行統計
 SQL> select to_char(sysdate,'ww') from dual group by to_char(sysdate,'ww');
TO
--
25
5。 按照每月進行統計
 SQL> select to_char(sysdate,'mm') from dual group by to_char(sysdate,'mm');
TO
--
06
6。 按照每季度進行統計
 SQL> select to_char(sysdate,'q') from dual group by to_char(sysdate,'q');
T
-
2
7。 按照每年進行統計
 SQL> select to_char(sysdate,'yyyy') from dual group by to_char(sysdate,'yyyy');
TO_C
----
2005
 8.要找到某月中所有周五的具體日期
 select to_char(td,'YY-MM-DD') from (
 select trunc(sysdate, 'MM')+rownum-1 as d
 from dba_objects
 where rownum < 32) t
 where to_char(td, 'MM') = to_char(sysdate, 'MM') --找出當前月份的周五的日期
 and trim(to_char(td, 'Day')) = '星期五'
--------
03-05-02
03-05-09
03-05-16
03-05-23
03-05-30
如果把where to_char(td, 'MM') = to_char(sysdate, 'MM')改成sysdate-90,即為查找當前月份的前三個月中的每週五的日期。
 9.oracle中時間運算
內容如下:
 1、oracle支持對日期進行運算
 2、日期運算時是以天為單位進行的
 3、當需要以分秒等更小的單位算值時,按時間進制進行轉換即可
 4、進行時間進制轉換時注意加括號,否則會出問題
 SQL> alter session set nls_date_format='yyyy-mm-dd hh:mi:ss';
會話已更改。
 SQL> set serverout on
 SQL> declare
 2 DateValue date;
 3 begin
 4 select sysdate into DateValue from dual;
 5 dbms_output.put_line('原時間:'||to_char(DateValue));
 6 dbms_output.put_line('原時間減1天:'||to_char(DateValue-1));
 7 dbms_output.put_line('原時間減1天1小時:'||to_char(DateValue-1-1/24));
 8 dbms_output.put_line('原時間減1天1小時1分:'||to_char(DateValue-1-1/24-1/(24*60)));
 9 dbms_output.put_line('原時間減1天1小時1分1秒:'||to_char(DateValue-1-1/24-1/(24*60)-1/(24*60*60))) ;
 10 end;
 11 /
原時間:2003-12-29 11:53:41
原時間減1天:2003-12-28 11:53:41
原時間減1天1小時:2003-12-28 10:53:41
原時間減1天1小時1分:2003-12-28 10:52:41
原時間減1天1小時1分1秒:2003-12-28 10:52:40
 PL/SQL過程已成功完成。

在Oracle中實現時間相加處理
 --名稱:Add_Times
 --功能:返回d1與NewTime相加以後的結果,實現時間的相加
 --說明:對於NewTime中的日期不予考慮
 --日期:2004-12-07
 --版本:1.0
 --作者:Kevin

 create or replace function Add_Times(d1 in date,NewTime in date) return date
is
 hh number;
 mm number;
 ss number;
 hours number;
 dResult date;
begin
 --下面依次取出時、分、秒
 select to_number(to_char(NewTime,'HH24')) into hh from dual;
 select to_number(to_char(NewTime,'MI')) into mm from dual;
 select to_number(to_char(NewTime,'SS')) into ss from dual;
 --換算出NewTime中小時總和,在一天的百分幾
 hours := (hh + (mm / 60) + (ss / 3600))/ 24;
 --得出時間相加後的結果
 select d1 + hours into dResult from dual;
return(dResult);
 end Add_Times;

 --測試用例
 -- select Add_Times(sysdate,to_date('2004-12-06 03:23:00','YYYY-MM-DD HH24:MI:SS')) from dual

在Oracle9i中計算時間差計算時間差是Oracle DATA數據類型的一個常見問題。  Oracle支持日期計算,你可以創建諸如“日期1-日期2”這樣的表達式來計算這兩個日期之間的時間差。

一旦你發現了時間差異,你可以使用簡單的技巧來以天、小時、分鐘或者秒為單位來計算時間差。 為了得到數據差,你必須選擇合適的時間度量單位,這樣就可以進行數據格式隱藏。
使用完善複雜的轉換函數來轉換日期是一個誘惑,但是你會發現這不是最好的解決方法。
 round(to_number(end-date-start_date))-消逝的時間(以天為單位)
 round(to_number(end-date-start_date)*24)-消逝的時間(以小時為單位)
 round(to_number(end-date-start_date)*1440)-消逝的時間(以分鐘為單位)
顯示時間差的默認模式是什麼? 為了找到這個問題的答案,讓我們進行一個簡單的SQL *Plus查詢。
 SQL> select sysdate-(sysdate-3) from dual;
SYSDATE-(SYSDATE-3)
-------------------
3
這裡,我們看到了Oracle使用天來作為消逝時間的單位,所以我們可以很容易的使用轉換函數來把它轉換成小時或者分鐘。 然而,當分鐘數不是一個整數時,我們就會遇到放置小數點的問題。
Select
(sysdate-(sysdate-3.111))*1440
from
dual;
(SYSDATE-(SYSDATE-3.111))*1440
------------------------------
4479.83333
當然,我們可以用ROUND函數(即取整函數)來解決這個問題,但是要記住我們必須首先把DATE數據類型轉換成NUMBER數據類型。
Select
round(to_number(sysdate-(sysdate-3.111))*1440)
from
dual;
ROUND(TO_NUMBER(SYSDATE-(SYSDATE-3.111))*1440)
----------------------------------------------
4480
我們可以用這些函數把一個消逝時間近似轉換成分鐘並把這個值寫入Oracle表格中。 在這個例子裡,我們有一個離線(logoff)系統級觸發機制來計算已經開始的會話時間並把它放入一個Oracle STATSPACK USER_LOG擴展表格之中。
Update
perfstat.stats$user_log
set
 elapsed_minutes =
round(to_number(logoff_time-logon_time)*1440)
where
 user = user_id
and
 elapsed_minutes is NULL;
查出任一年月所含的工作日
 CREATE OR REPLACE FUNCTION Get_WorkingDays(
 ny IN VARCHAR2
 ) RETURN INTEGER IS
 /*------------------------------------------------ ------------------------------------------
函數名稱:Get_WorkingDays
中文名稱:求某一年月中共有多少工作日作者姓名: XINGPING
編寫時間: 2004-05-22
輸入參數:NY:所求包含工作日數的年月,格式為yyyymm,如200405
返回值:整型值,包含的工作日數目。
算法描述:
 1).列舉出參數給出的年月中的每一天。 這裡使用了一個表(ljrq是我的庫中的一張表。這個表可以是有權訪問的、記錄條數至少為31的任意一張表或視圖)來構造出某年月的每一天。
 2).用這些日期和一個已知星期幾的日期相減(2001-12-30是星期天),所得的差再對7求模。 如果所求年月在2001-12-30以前,那麼所得的差既是負數,求模後所得值範圍為大於-6,小於0,如-1表示星期六,故先將求模的結果加7,再求7的模.
 3).過濾掉結果集中值為0和6的元素,然後求count,所得即為工作日數目。
 -------------------------------------------------- -----------------------------------------------*/
 Result INTEGER;
BEGIN
 SELECT COUNT(*) INTO Result
 FROM (SELECT MOD(MOD(q.rq-to_date('2001-12-30','yyyy-mm-dd'),7),7) weekday
 FROM ( SELECT to_date(ny||t.dd,'yyyymmdd') rq
 FROM (SELECT substr(100+ROWNUM,2,2) dd
 FROM ljrq z WHERE Rownum<=31
 ) t
 WHERE to_date(ny||t.dd,'yyyymmdd')
 BETWEEN to_date(ny,'yyyymm')
 AND last_day(to_date(ny,'yyyymm'))
)q
 ) a
 WHERE a.weekday NOT IN(0,6);
 RETURN Result;
 END Get_WorkingDays;
______________________________________
還有一個版本
 CREATE OR REPLACE FUNCTION Get_WorkingDays(
 ny IN VARCHAR2
 ) RETURN INTEGER IS
 /*------------------------------------------------ -----------------------------------------
函數名稱:Get_WorkingDays
中文名稱:求某一年月中共有多少工作日作者姓名: XINGPING
編寫時間: 2004-05-23
輸入參數:NY:所求包含工作日數的年月,格式為yyyymm,如200405
返回值:整型值,包含的工作日數目。
算 法描述:使用Last_day函數計算出參數所給年月共包含多少天,根據這個值來構造一個循環。 在這個循環中先求這個月的每一天與一個已知是星期天的日期(2001-12-30是星期天)的差,所得的差再對7求模。 如果所求日期在2001-12-30以前,那麼所得的差既是負數,求模後所得值範圍為大於-6,小於0,如-1表示星期六,故先將求模的結果加7,再求7 的模.如過所得值不等於0和6(即不是星期六和星期天),則算一個工作日。
 -------------------------------------------------- --------------------------------------*/
 Result INTEGER := 0;
 myts INTEGER; --所給年月的天數
 scts INTEGER; --某天距2001-12-30所差的天數
 rq DATE;
 djt INTEGER := 1; --
BEGIN
 myts := to_char(last_day(to_date(ny,'yyyymm')),'dd');
LOOP
 rq := TO_date(ny||substr(100+djt,2),'yyyymmdd');
 scts := rq - to_date('2001-12-30','yyyy-mm-dd');
 IF MOD(MOD(scts,7)+7,7) NOT IN(0,6) THEN
 Result := Result + 1;
 END IF;
 djt := djt + 1;
 EXIT WHEN djt>myts;
 END LOOP;
 RETURN Result;
 END Get_WorkingDays;
以上兩個版本的比較
第一個版本一條SQL語句就可以得出結果,不需要編程就可以達到目的。 但需要使用任意一張有權訪問的、記錄條數至少為31的一張表或視圖。
第二個版本需要編程,但不需要表或者視圖。
這兩個版本都還存在需要完善的地方,即沒有考慮節日,如五一、十一、元旦、春節這些節假期都沒有去除。 這些節假日應該維護成一張表,然後通過查表來去除這些節假日。


求一段連續日期,但排除六日
自已標記一下,方便以後查尋

select allday from (
select to_date('2007-02-15','yyyy-mm-dd')+rownum allday from
(select to_date('2007-02-15','yyyy-mm-dd'),to_date('2007-02-28','yyyy-mm-dd') from dual)
connect by rownum < to_date('2007-02-28','yyyy-mm-dd')-to_date('2007-02-15','yyyy-mm-dd')
) where to_char(allday,'D') >1 and to_char(allday,'D') <7 

轉自 http://oracled2k.pixnet.net/blog/post/21677874-若有侵權請告知

2009年12月17日 星期四

巧妙的運用 AfterPositionChange 事件 來達到透過上一筆、下一筆來瀏覽RDB資料。

以下範例,是透過LSXODBC 將RDB 資料顯示在表單的表格中,且巧妙的使用AfterPositionChange 事件 來達到與一般設計介面一樣,在表單上使用「下一筆」、「上一筆」按鈕來輕鬆瀏覽RDB後端資料 ;可達到將AP資料完全存放在Oraclec或是其它可與Domino一起應用的資料庫軟體上,目前我是將他與Oracle一起應用。

Uselsx "*LSXODBC"
Dim con As ODBCConnection
Dim qry As ODBCQuery
Dim result As ODBCResultSet
Sub Postopen(Source As Notesuidocument)
  Set con = New ODBCConnection
  Set qry = New ODBCQuery
  Set result = New ODBCResultSet
  Set qry.Connection = con
  Set result.Query = qry
  On Event AfterFirstRow From result Call
  AfterPositionChange
  On Event AfterLastRow From result Call AfterPositionChange
  On Event AfterNextRow From result Call AfterPositionChange
  On Event AfterPrevRow From result Call AfterPositionChange
  con.ConnectTo("ATDB")
  qry.SQL = "SELECT * FROM STUDENTS ORDER BY LASTNAME"
  result.Execute
  If Not source.EditMode Then
    source.EditMode = True
  End If
  result.FirstRow
  Call source.FieldSetText("Student_No", _
  Cstr(result.GetValue("STUDENT_NO")))
  Call source.FieldSetText("FirstName", _
  result.GetValue("FIRSTNAME"))
  Call source.FieldSetText("LastName", _
  result.GetValue("LASTNAME"))
  Call source.FieldSetText("Address", _
  result.GetValue("ADDRESS"))
  Call source.FieldSetText("City", _
  result.GetValue("CITY"))
  Call source.FieldSetText("State", _
  result.GetValue("STATE"))
  Call source.FieldSetText("Zip", _
  result.GetValue("ZIP"))
  Call source.FieldSetText("Phone", _
  result.GetValue("PHONE"))
  Call source.FieldSetText("Cr_to_date", _
  Cstr(result.GetValue("CR_TO_DATE")))
End Sub
Sub Click(Source As Button)
REM Action to get the next row
  Dim workspace As New NotesUIWorkspace
  Dim uidoc As NotesUIDocument
  Set uidoc = workspace.CurrentDocument
  If Not result.IsEndOfData Then result.NextRow
  Call uidoc.FieldSetText("Student_No", _
  Cstr(result.GetValue("STUDENT_NO")))
  Call uidoc.FieldSetText("FirstName", _
  result.GetValue("FIRSTNAME"))
  Call uidoc.FieldSetText("LastName", _
  result.GetValue("LASTNAME"))
  Call uidoc.FieldSetText("Address", _
  result.GetValue("ADDRESS"))
  Call uidoc.FieldSetText("City", result.GetValue("CITY"))
  Call uidoc.FieldSetText("State", result.GetValue("STATE"))
  Call uidoc.FieldSetText("Zip", result.GetValue("ZIP"))
  Call uidoc.FieldSetText("Phone", result.GetValue("PHONE"))
  Call uidoc.FieldSetText("Cr_to_date", _
  Cstr(result.GetValue("CR_TO_DATE")))
End Sub
Sub Click(Source As Button)
REM Action to get the previous row
  Dim workspace As New NotesUIWorkspace
  Dim uidoc As NotesUIDocument
  Set uidoc = workspace.CurrentDocument
  If Not result.IsBeginOfData Then result.PrevRow
  Call uidoc.FieldSetText("Student_No", _
  Cstr(result.GetValue("STUDENT_NO")))
  Call uidoc.FieldSetText("FirstName", _
  result.GetValue("FIRSTNAME"))
  Call uidoc.FieldSetText("LastName", _
  result.GetValue("LASTNAME"))
  Call uidoc.FieldSetText("Address", _
  result.GetValue("ADDRESS"))
  Call uidoc.FieldSetText("City", result.GetValue("CITY"))
  Call uidoc.FieldSetText("State", result.GetValue("STATE"))
  Call uidoc.FieldSetText("Zip", result.GetValue("ZIP"))
  Call uidoc.FieldSetText("Phone", result.GetValue("PHONE"))
  Call uidoc.FieldSetText("Cr_to_date", _
  Cstr(result.GetValue("CR_TO_DATE")))
End Sub
Sub Queryclose(Source As Notesuidocument, Continue As Variant)
  result.Close(DB_CLOSE)
  con.Disconnect
End Sub
Sub AfterPositionChange(res As ODBCResultSet)
  Dim ws As New NotesUIWorkspace
  Dim source As NotesUIDocument
  Set source = ws.CurrentDocument
  Call source.FieldSetText("RowNumber", Cstr(res.CurrentRow))
End Sub

引用自Domino Designer 6.5 Help