2011年8月27日 星期六

[ SQL ] GROUP BY, HAVING

GROUP BY
如果有需求,是要得到某某分類的資料加總起來的數字是多少,可以利用GROUP BYSUM函式輕鬆得到解法。
比方說,一個資料表裡面有很多各種顏色的椅子,每張椅子都有各自的價錢,那麼我想知道各種顏色椅子的總價...
SELECT chair.color, SUM(chair.price)
FROM chair
GROUP BY chair.color

HAVING
這個語法要跟上面的GROUP BY一起使用。如果要把上述查詢出來的結果再進一步去過濾,就可以利用這個語法設定條件(概念上有點像WHERE)。
承上述例子,我想知道各種顏色椅子總價在30000以上的結果,那麼可以....
SELECT chair.color, SUM(chair.price)
FROM chair
GROUP BY chair.color
HAVING SUM(chair.price) > 30000
這個語法也可以用於過濾文字....
SELECT chair.color, SUM(chair.price)
FROM chair
GROUP BY chair.color
HAVING chair.color = 'Red'

2011年8月17日 星期三

[ C# ] BeginTransaction, Rollback and Commit

C#指令版
http://blog.yam.com/kosaten/article/13648132
http://my.so-net.net.tw/idealist/CS/Basic/transaction.html
SQL指令版
http://msdn.microsoft.com/zh-tw/library/ms181299.aspx
不曉得你們看不看得到這些連結
簡單來說,SQL裡有一個概念叫Rollback,大陸那裡好像翻作「回滾」,這個動作能夠讓資料庫的狀態回到被定義開始交易之前。
SQL有這個指令,C#也有類似的元件可以操作。詳細的操作方式連結裡面都有。
我的想法是,在進行大量修改和寫入(UPDATE和INSERT)的時候,為了避開伺服器中斷而導致輸入資料不齊全的問題,有必要加上這個機制來躲過這個危機。SELECT並不會更動到資料表的狀態,所以不需要。那只有一筆的寫入或更新,我還在考慮要不要使用這個動作,因為沒有經驗和資訊指出這個動作會不會對資料庫的效能和負擔造成影響。
另外,我還不太會寫SP,所以這裡應該會是在C#來實現這個概念。

如果看不到上述的連結,請跟我說一下,我把裡面的資料摘錄下來再另外公佈。

=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:


好了,我說明一下我這裡測試的方法和結論。
方法:
1. 試著把新資料寫進資料表裡,然後再rollback回去,看能不能回到交易前的狀態。結論是可以。
2. 試著把新資料寫進資料表裡,然後再commit。看交易結束之後資料有沒有留著。結論是也可以。
3. 寫入資料之後的指令加入中斷點,看看rollback前和rollback後的結果。結果是發現,我在中斷點的時候去開SQL Server management studio直接敲select指令抓資料,是沒辦法抓的,指令會一直跑跑跑沒有回應。我想應該是資料庫把狀態鎖起來了,除了該連結之外,其它連結不被允許進入資料表進行存取。
4. 試著把新資料寫進資料表裡,然後把SQL server的服務徹底關掉,程式完成之後再打開,看會發生什麼事。因為沒有用try-catch,所以瀏覽器上是會顯示錯誤畫面,但是資料庫的狀態是在交易開始之前,也就是資料沒有被寫入。

結論:
這個方法確實可以達到我們一開始的想法:避開在進行資料大量寫入或修改的時候,資料庫伺服器當機或中斷所引發的資料有多有少的情況。
適度地使用try-catch-finally,就可以實現這個概念。

SqlConnection sqlConn = DB.Conn();
sqlConn.Open();
SqlTransaction sqlTrans = sqlConn.BeginTransaction();
SqlCommand sqlComm.Connection = sqlConn;
sqlComm.Transcation = sqlTrans;

sqlConn = DB.Conn();
sqlConn.Open();
sqlTrans = sqlConn.BeginTransaction();
sqlComm.Connection = sqlConn;
sqlComm.Transcation = sqlTrans;
try{
	for(;;){
		strInsertion = "INSERT INTO xxx (a, b, c, d) VALUE ('A', 'B', 'C', 'D')";
		sqlComm.CommandText = strInsertion;
		sqlComm.ExcuteNonQuery();
	}
	sqlTrans.Commit();
}
catch{
	sqlTrans.Rollback();
}
finally{
	sqlConn.Close();
}

=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:

2011/08/20 新增關於BeginTransaction, Rollback, Commit的測試:
問題:在寫入資料表的時候關掉網頁,對資料表的狀態?
測試方式:
寫一隻無限迴圈程式,不斷讓程式對資料表寫不同的資料,然後在執行的過程中關掉網頁。
測試結果:
一開始寫程式的時候沒有用try-catch-finally,所以直接把網頁關掉,資料表會被lock起來,強制把IDE的模擬Server程式關掉後才得以解開。資料沒有寫進去。
後來加入try-catch-finally後,執行的過程把網頁關掉,再去查詢資料表,是可以運作的。資料也沒有寫進去,
由測試結果的推論:
try-catch-finally,是去執行try裡面的動作,遇到Exception的時候再強制跳到catch,無論try完或是catch完,最後都執行finally。直接把網頁關掉,也能夠用這個方式catch出來。
SQL的做法應該是在先把資料表的狀態lock住,然後對資料表的修改和寫入都是寫到一個暫存的地方,等到下commit指令以後,才把結果一次倒進資料表裡。
總之,今天討論到,如果程式執行很久,使用者在不知情(或是手賤)的情況下關掉網頁,以這種做法來說,不用擔心資料表會被持續lock住。

2011年3月28日 星期一

[PHP] 上一頁的網址

在寫網頁程式的時候,常常會在某個時候用到"回上一頁"的功能。
雖然講得好像只要按瀏覽器左上角那個大大的箭頭就搞定了,但還是沒辦法滿足所有的需求,像是執行完一段程式之後要把畫面轉到前一張網頁去的時候,就沒辦法光靠左上角那個箭頭來完成。
而且,這樣的做法很爛,很沒有一個"系統"的樣子(為什麼一個稱為"系統"的東西,遇到沒有接下來的頁面而要回上一頁的時候還得要使用者自己點?)
像是在Bookle裡面,它的右上角一直有個login的連結可以點。我們寫程式的並不能知道使用者或從哪個頁面點到login,但是又希望經過login以後能夠回到正在瀏覽的頁面,"回到前一頁"的動作就變得不可或缺了。


在PHP裡,$_SERVER有很多有意思參數可以玩。
其中,$_SERVER["HTTP_REFERER"]就是指連到該頁面的前一個頁面是誰。
值得注意的是.....如果網址是用自己key的,這個參數就抓不到什麼東西。
所以要測試的話,要寫個兩張網頁a, b,從a連到b,b網頁裡叫出這個參數就可以看到a了。

by the way, referer這個字在一些網頁語言裡都有"連到該網頁的前一張網頁",也就是"上一頁"的意思。
asp有document.referer
php有$_SERVER["HTTP_REFERER"]

只是要回上一頁的話,javascript也有簡單的語法可以用:history.back(1),但是它的原理和referer不一樣。
referer是在網頁的標頭裡找"我是從哪個網址連過來的";而javascript用的這個history這個物件(好像有點像堆疊)裡把上一頁的網址直接抓出來。

不過referer這個參數好像不是這麼萬能,至少在php manual裡就明白寫出來這點了。
原因在此:[用PHP偽造網站referer地址]
簡單來說,就是可以用socket來修改網頁的標頭,改掉referer這個欄位裡的參數。








不過我在寫Bookle的時候就沒考慮這麼多了啦~ 只是一個小東西,沒必要為了細小的安全性問題把自己搞得很暈。
以後有需要的時候再來細細研究。

2011年3月14日 星期一

[系統開發] Bookle - 資料庫建置

不太會用Microsoft Office Visio來畫ERD和關聯綱目,又不想慢慢用Word或Powerpoint畫,乾脆用文字描述。看不清楚我也不管了。

資料庫:bookmanage
資料表
book
reflink
note
tag
stick
people
update_log

book: 書籍本身的資料,裡面有作者、目錄、摘要、ISBN和建檔的用戶ID
reflink: 書籍的參考連結,屬於book資料表的一對多屬性。
note: 書籍的筆記,屬於book資料表的一對多屬性。
tag: 標籤。
stick: book和tag的關係,代表標籤在書上。
people: 用戶。只是一個簡單的用戶系統。設計上是,只有該系統的用戶才能建檔,並且只有原建檔者才能在書上寫筆記。

--

大概就是如此。

不清楚也沒辦法了。
我也懶得花心神再畫什麼圖畫什麼文件在一個破爛的自我練習系統上。

[系統開發日誌] 2011.03.13 - 會員登入系統

前置作業當然是要先在MySQL裡做好資料庫和各個資料表,這部分的記錄我日後再補。

總之,今天我算是完成了會員登入這一部分了。東西不難,但是函式太久沒用,程式太久沒寫,還算是生疏了。
會做這個記錄,主要是希望我可以透過這份記錄,日後如果又在遇到同樣的問題時,可以很快地找出來解決的方法。

  1. session_start():有任何一張網頁需要用到session裡的資料時,甚至是要除掉session資料,都得先宣告session_start();。[PHP]
  2. session_start():在宣告session_start()前,絕對不能先輸出任何東西到網頁上,包括。最保險的方法是在第一行就先宣告它。[PHP]
  3. text-align:無法用在table標籤裡。如果要在table標籤裡設定置中,要用margin-leftmargin-right控制左右空白寬度來完成。[CSS]


2011年3月11日 星期五

[系統開發] Bookle - 前置作業

我決定稱它為Bookle,很明顯就是自以為能和Google攀親帶故的名稱。

前言:
身為一個研究生,以及即將要在科技業裡打滾的人,看過很多書是很合邏輯的。這些書包括技術書、規格書、工具書、雜誌、閒書、漫畫、Playboy...
人腦畢竟是人腦,看過書之後不太可能說真的馬上就把裡面的技術學得驚天動地的。很多時候只是腦子裡有點印象而已,等到真的要再引用的時候,再回頭去把要用的資料挖出來
注意!挖資料這個動作在描敘上頗為輕描淡寫,但是手頭上如果有一大堆書,尤其是類型類似的書,真的要很快地把想要的資料找出來不是一件容易的事。
因此,我認為如果把手頭上的書都先在資料庫裡建檔建好,有需要的時候再利用資料庫存取的程式去搜尋,就可以很快地知道我需要的書是哪些。
當然,我不可能有這麼多的閒工夫把每一本書都一字不漏地key進資料庫裡。所以,我打算用標籤系統(tag)筆記(note)來完成這項工作。
利用標籤系統在每本書上面做大小數量不等的註記,可以很方便地將每本書作一個簡易的分類,同時也可以利用標籤的搜尋,來達到快速取得相關書籍資料的目的;而筆記系統可允許使用者在每本書的相關資料裡作筆記,雖然沒辦法把書裡的每個章節每個字句全寫進去(時間成本太高),但是可以利用一點筆記讓自己增加些印象,並且在搜尋的過程能有更多的依據。

需求:
1. 每本書要能建檔,內容要有書名、作者、ISBN、摘要、目錄、標籤、筆記、參考連結。
2. 基本搜尋是利用書名、摘要、目錄、標籤、筆記作為搜尋依據;進階搜尋要能夠自訂搜尋方法。
3. 任何人皆可瀏覽,但只有會員才能新增、修改資料。
4. 儘可以做到高可用性,使用起來舒服。(介面簡單清楚漂亮,使用上不傷眼)

開發環境:
1. Web-based系統,利用瀏覽器做執行和存取。
2. 程式語言: PHP
3. 資料庫: MySQL
4. 伺服器驅動程式: Apache
(其實就只是去下載套裝好的AppServ最新release版再設定一下而已。開發程式就已經夠辛苦的了,伺服器建置上就別太花精神了....