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住。