2009年3月24日 星期二

自寫 rownum 的方法

Oracle SQL有個 rownum 的內建功能, 可以將select 的結果自動編號.


例如:找出以下表格欄位TEST1.L為偶數的資料列, 並給予編號如下,
 
select rownum, L, P, Q from test1 where mod(L,2)=0;
 
ROWNUM L P Q
--------- --------- - ---------
1 2 B 2
2 4 C 3
3 6 A 3
4 8 G 3


其實也可以用以下SQL語法,達到相同的目的,
 
select count(y.L) my_rownum, x.L, x.P, x.Q
from test1 x, test1 y
where x.L >= y.L
and mod(x.L,2)=0 --->
and mod(y.L,2)=0 ---> 注意:x,y條件皆相同
group by x.L, x.P, x.Q;
 
MY_ROWNUM L P Q
--------- --------- - ---------
1 2 B 2
2 4 C 3
3 6 A 3
4 8 G 3
 
PS:這種自身join的方法,也或許可以用在其他資料庫或不支援rownum的情況.
 
再加一個範例: 相同x.cus_id, 按照 x.risk,x.bra_id,x.pr_rank_name,x.pro_name 排名
 
select count(y.cus_id) as num,
x.cus_id,x.risk,x.bra_id,x.pr_rank_name,x.pro_name
from aaa x, aaa y
where x.cus_id = y.cus_id
and ((x.risk > y.risk) or
((x.risk = y.risk) and (x.bra_id > y.bra_id)) or
((x.risk = y.risk) and (x.bra_id = y.bra_id) and (x.pr_rank_name > y.pr_rank_name)) or
((x.risk = y.risk) and (x.bra_id = y.bra_id) and (x.pr_rank_name = y.pr_rank_name) and (x.pro_name > y.pro_name )) or
((x.risk = y.risk) and (x.bra_id = y.bra_id) and (x.pr_rank_name = y.pr_rank_name) and (x.pro_name = y.pro_name ))
)
group by x.cus_id,x.risk,x.bra_id,x.pr_rank_name,x.pro_name

2009年3月23日 星期一

各種免費網路資源

免費資源:
http://www.slime.com.tw/freeResource.htm

免費的網路傳檔硬碟: (單檔限制/刪檔時限/語系/註冊)
file dropper 檔案上傳 (5G/30天/英文/不需註冊): http://www.filedropper.com/
FlyUpload 免費網路硬碟(2G/30天/英文/不需註冊): http://www.flyupload.com/
iFile.it 免費網路硬碟(100M/30天/繁中/不需註冊): http://ifile.it/
MediaFire (100M/永不刪檔/英文/免費註冊): http://www.mediafire.com/
BaDonGo(1G/永不刪檔/多語系/免費註冊): http://www.badongo.com/

免費的貼圖空間:
XS.To: http://xs.to/
http://xs537.xs.to/xs537/09120/c15300885.gif
Pict.com: http://www.pict.com/
http://img2.pict.com/ee/1a/1f/d8c4219dc8d580b4f3519f4ae7/wrpgY/c15300.gif

轉換工具 - 網路資源

PDF Converter 提供線上 PDF 與 Office 文件(網頁文件) 的相互格式轉換:
http://www.freepdfconvert.com/

Remove PDF passwords and restrictions (such as printing, copying text, etc.)
http://freemypdf.com/

Super Screenshot 線上網頁抓取工具 :
http://www.superscreenshot.com/
PS: 測試結果, 大致都不錯, 只是部落格程式框的圖片位置有稍微跑掉,可惜.

2009年3月19日 星期四

ACCESS 排名取值

ACCESS 沒有ROW_NUMBER()的功能,因此若要排名次則改用如下的方式,
CASE 1: 在相同購買物品項目中,找出最後2次購買的資料:

SELECT X.購買物品, X.第幾次, X.價格, count(Y.購買物品) AS Rank, (X.購買物品 & '-' & X.第幾次 ) as ID
FROM DataTable AS X, DataTable AS Y
WHERE X.購買物品 = Y.購買物品 and
(X.第幾次 < Y.第幾次 or (X.購買物品 = Y.購買物品 and X.第幾次= Y.第幾次 ))
GROUP BY X.購買物品, X.第幾次, X.價格
HAVING count(Y.購買物品) <= 2;

CASE 2: 在相同購買物品項目中,僅保留最後2次購買的資料:

Delete from DataTable where (購買物品 & '-' & 第幾次 ) not in
(
SELECT (X.購買物品 & '-' & X.第幾次 ) as ID
FROM DataTable AS X, DataTable AS Y
WHERE X.購買物品 = Y.購買物品 and
(X.第幾次 < Y.第幾次 or (X.購買物品 = Y.購買物品 and X.第幾次= Y.第幾次))
GROUP BY X.購買物品, X.第幾次, X.價格
HAVING count(Y.購買物品) <= 2
)
PS:若是DataTable有主索引,則更方便操作.

2009年3月13日 星期五

SQL 流水號字串補零

輸出 "YYYYMMDD"
1. CONVERT(varchar(8), GETDATE(), 112)

輸出 "0123"
1. REPLICATE('0', 4 - LEN(CONVERT(char(4), 123))) + CONVERT(char(4), 123)
2. RIGHT('0000' + CONVERT(varchar(4), 123), 4)
3. RIGHT('0000' + CAST(123 as varchar), 4)

輸出結果:YYYYMMDD流水號 =>
1. CONVERT(varchar(8), GETDATE(), 112) + RIGHT('0000'+CAST(續號 as varchar),4)

PS:CAST 像是 CONVERT 的陽春版了.

ACCESS 多重 JOIN 疑問

ACCESS 不能同時使用兩個LEFT JOIN 嗎? 疑 ?
怎麼試都不成功...
SELECT CC.編號,CC.品名,AA.表單A數量和,BB.表單B數量和,(AA.表單A數量和-BB.表單B數量和) AS 兩表數量差異
FROM
(SELECT 表單A.編號,表單A.品名 FROM 表單A UNION SELECT 表單B.編號,表單B.品名 FROM 表單B ) AS CC
LEFT JOIN (SELECT 表單A.編號,表單A.品名,SUM(表單A.數量) AS 表單A數量和 FROM 表單A GROUP BY 表單A.編號,表單A.品名) AS AA ON CC.編號 = AA.編號
LEFT JOIN (SELECT 表單B.編號,表單B.品名,SUM(表單B.數量) AS 表單B數量和 FROM 表單B GROUP BY 表單B.編號,表單B.品名) AS BB ON CC.編號 = BB.編號

最後改變方法...把資料都先UNION ALL 起來, 雖然WORKing, 但,這種邏輯是否是當呢? 記憶體...效能...
SELECT 編號,
品名,
SUM(IIF(ST='A',數量,0)) AS 表單A數量,
SUM(IIF(ST='A',0,數量)) AS 表單B數量,
abs(SUM(IIF(ST='A',數量,數量*-1))) AS 兩表數量差異
FROM (SELECT 'A' AS ST, 編號, 品名, 數量 FROM 表單A
UNION ALL
SELECT 'B' AS ST, 編號, 品名, 數量 FROM 表單B
) AS CC
GROUP BY 編號, 品名

PS: 其他SQL 可以用case when 替換 iif 達到類似的目的.

2009年3月11日 星期三

Row_number 的使用

利用分析函數,求得排名, 再利用排名,來控制資料輸出格式.(以下為MsSQL語法,Oracle 也有類似功能)
SELECT (CASE WHEN rank2 = 1 THEN aaa ELSE '' END) AS BreakA, bbb 
--> 相同的 aaa 只出現一次,像作報表一樣
FROM
(SELECT c.aaa, c.bbb,
row_number() OVER
(partition BY c.aaa, c.bbb ORDER BY c.aaa, c.bbb) AS rank1,
row_number() OVER
(partition BY c.aaa ORDER BY c.aaa, c.bbb) AS rank2
FROM dbo.ItemA AS a LEFT OUTER JOIN dbo.ItemC AS c
ON a.ItemID = c.ItemID) AS g
WHERE rank1 = 1 ---> 類似 distinct 的效果
ORDER BY aaa, bbb

註:
row_number():逐列排名,重複資料,不允許相同排名,仍有第1,2名之分.
rank():允許有2個第1名,此時就沒有第2名,直接跳第3名.
dense_rank():允許有2個第1名,第3個則為第2名,即名次不跳號.
ACCESS 不支援,其他資料庫尚未測過.
ACCESS 可以參考自身JOIN的方法達到類似的目的.

2009年3月10日 星期二

Oracle Trunc 函數

TRUNC()函數分兩種: TRUNC(for number) 和 TRUNC(for date)
TRUNC(number[,n]): 捨去指定小數點位數後的值. 註:n若為負數則會往前捨去小數前的位數.
TRUNC(date[,fmt]): 捨去指定參數後的日期時間. fmt:'YY','MM','DD','HH','MI'等

2009年3月9日 星期一

SQL Update 特殊寫法

利用 Case When 選擇性的update 欄位內容 (Oracle 8.1.7, MsSQL, MySQL 適用)
Oracle 判斷的較不嚴謹, null 與 空字串看來相同...

表格T1:
SQL> select * from t1;

USER_ID CLASS
--------- --------------------
1 A
2
3 C

語法:
SQL> update t1 set class = (case when class is null then 'NullValue' else class end);

3 rows updated.

結果:
SQL> select * from t1;

USER_ID CLASS
--------- --------------------
1 A
2 NullValue
3 C

MsSQL 與 MySQL 的用法,有分空字串與NULL

update t1 set class = (case when class='' then 'EmptyValue' else class end);
update t1 set class = (case when class is null then 'NullValue' else class end);


於Oracle 還有decode 的用法,可以達到雷同的效果,
update t1 set class = decode(class,null,'NULLVALUE',class);
於ACCESS 還有iif 的用法,可以達到雷同的效果,
Private Sub do_sql_Click()
On Error GoTo Err_do_sql_Click


Dim SQL As String

'OK: 但沒有必要多加判斷
SQL = "UPDATE [員工資料] " & _
"SET [部門代號] = iif([部門代號]='' or [部門代號] is null ,'NullValue',[部門代號]) " & _
"WHERE [工號] = '908' "

'NG: 以空字串判斷讀不到新增record的 null也讀不到修改後的空白欄位
SQL = "UPDATE [員工資料] " & _
"SET [部門代號] = iif([部門代號]='','空字串',[部門代號]) " & _
"WHERE [工號] = '908' "

'OK! 測試發現, ACCESS 不認空字串'' 型態,用null即可包含空字串.
SQL = "UPDATE [員工資料] " & _
"SET [部門代號] = iif([部門代號] is null,'nullvalue',[部門代號]) " & _
"WHERE [工號] = '907' "

DoCmd.RunSQL SQL
MsgBox (SQL)


Exit_do_sql_Click:
Exit Sub

Err_do_sql_Click:
MsgBox Err.Description
Resume Exit_do_sql_Click

End Sub

補充:
於 MsSQL 必須留意, 空字串''與 NULL 是不同的!
空字串於 CASE WHEN 可以用 CLASS = '' 表示比較條件式,
NULL 於 CASE WHEN 應該用 CLASS IS NULL 表示條件式, 或利用 ISNULL(CLASS,'NULLVALUE')轉換之.

PS: 其他資料庫尚未實測.

2009年3月6日 星期五

SQL Group By 直轉橫

Oracle SQL, MySQL, 通用的語法, 一般的group by 統計方式,為直列式由上而下逐筆顯示,
以下方式, 通常用於將特定已知的"資料",轉向作為[欄位]後,進行分析統計.
簡單的說, 就是原來直放的資料,變成橫放.
範例:
按各個userid, 橫向呈現於各個 CLASS_A,B,C 統計分析相關資料出現的次數.
SELECT user_id, sum(CLASS_A) AS CLASS_A,
sum(CLASS_B) AS CLASS_B,
sum(CLASS_C) AS CLASS_C
FROM ( select user_id,
(CASE WHEN CLASS = 'a' THEN 1 ELSE '' END) AS CLASS_A,
(CASE WHEN CLASS = 'b' THEN 1 ELSE '' END) AS CLASS_B,
(CASE WHEN CLASS = 'c' THEN 1 ELSE '' END) AS CLASS_C
from T1
)
GROUP BY USER_ID
ORDER BY USER_ID;


簡化的作法,結果一樣....

SELECT user_id,
sum(CASE WHEN CLASS = 'a' THEN 1 ELSE '' END) AS CLASS_A,
sum(CASE WHEN CLASS = 'b' THEN 1 ELSE '' END) AS CLASS_B,
sum(CASE WHEN CLASS = 'c' THEN 1 ELSE '' END) AS CLASS_C
FROM T1
GROUP BY USER_ID
ORDER BY USER_ID;

PS:若是ACCESS 資料庫, case when 換成iff 的語法即可,
如: SUM(IIF(CLASS = 'a', 1, 0))

SQL Case When 的使用

Oracle SQL, MySQL, MsSQL 通用的語法, 可以選擇性的加工輸出欄位值:
範例 1:

select user_id,
   (CASE WHEN CLASS = 'a' THEN 1 ELSE '' END) AS CLASS_A,
   (CASE WHEN CLASS = 'b' THEN 1 ELSE '' END) AS CLASS_B,
   (CASE WHEN CLASS = 'c' THEN 1 ELSE '' END) AS CLASS_C
from T1;

範例 2:

select id, (CASE WHEN 數據 = 0 THEN '沒有數據' ELSE 數據 END) AS "數據值"
from T2;

PS:還可以用於 update set=(case when...), where xxx=(case when...) 敘述中

2009年3月4日 星期三

旅夜書懷

旅夜書懷       唐 杜甫
-------------------
細草微風岸,危檣獨夜舟,
星垂平野闊,月湧大江流。
名豈文章著,官應老病休,
飄飄何所似,天地一沙鷗。

春暮

春暮               作者: 曹豳
------------------------
門外無人問落花,綠陰冉冉遍天涯。

林鶯啼到無聲處,青草池塘獨聽蛙。

靈格斯詞霸綠色便攜版

Lingoes 靈格斯詞霸 是一款簡明易用的詞典與文本翻譯軟體,
其綠色便攜版本, 無需安裝即可使用, 內置基礎英漢詞典, 即時翻譯等。
官網: http://www.lingoes.cn/zh/index.html
下載: http://www.lingoes.cn/zh/translator/trans_downsoft.php?id=69