Advanced usage of SQL
SQL
不僅能進行簡單的查詢資料,還能進行更多細節設定與操作。
Table of Contents
- Custom Connection
- Exec Stored Procedure
- Detail Control
- Transaction
- Writing Large Amounts Of Data
- More
Custom Connection
除了在Congfig中設定預設的連線資訊之外,也可以直接從程式碼中指定
示範:
string Host = "127.0.0.1"; // 資料庫位置
string DBName = "Fpage"; // 資料庫名稱
string User = "fpage"; // 連線帳號
string Password = "I7890"; // 連線密碼
SQL db = new SQL(Host, DBName, User, Password); // 建立資料庫存取物件
db.Timeout = 15; // 設定連線逾時秒數,預設 15 秒
db.ApplicationIntent = "ReadWrite"; // 該連線所需意圖,預設 ReadWrite
db.MultiSubnetFailover = false; // 是否啟用容錯移轉叢集執行個體,預設 false
db.Encrypt = false; // 是否啟用加密,預設 false
db.TrustServerCertificate = false; // 連結到執行個體時使用憑證,預設 false
object[] o = db.quickQuery<object>("select * from class"); // 查詢
v1.20
也可以使用自訂的連線字串(Connection String)進行連線
SQL db = new SQL("Data Source=(LocalDb)\MSSQLLocalDB;Initial ..."); // 使用自訂 Connection String 連線
備註:
Timeout
,ApplicationIntent
,MultiSubnetFailover
... 等屬性,多數情況不需設定
Exec Stored Procedure
以下範例將示範使用 quickExec()
執行 Stored Procedure,假設有個 Stored Procedure 像是這樣
輸入:
act
(string)passportcode
(string)
輸出:
res
(int)
create procedure [dbo].[xp_checklogin]
@act nvarchar(100),
@passportcode nvarchar(64),
@res int output
as
begin
...
定義輸出資料的資料模型 (Model),注意屬性名稱需相同
示範:
class ModelOutput
{
public int res { get; set; }
};
執行 Stored Procedure,並取得輸出的資料自動填充到 Model 裡,最後自動關閉連線
示範:
SQL db = new SQL();
// 輸入參數
var input_para = new
{
act = "admin",
passportcode = "123456789"
};
// 輸出參數定義 (使用 System.Data.SqlDbType 對應資料型態)
var output_para = new
{
res = SqlDbType.Int
};
// 執行 Stored Procedure
ModelOutput result = db.quickExec<ModelOutput>("xp_checklogin",input_para,output_para);
if (result == null) // 判斷是否執行成功
Console.WriteLine(db.getErrorMessage()); // 執行出錯,印出錯誤訊息
else
Console.WriteLine(result.res); // 執行成功,印出輸出資料
備註
Detail Control
以下將示範滿足複雜需求,同時建立兩個 SQL 連線,第一個連線取出資料,並使用第二個連線寫入資料,挑戰如下:
- 同時開啟兩個 SQL 連線並交換資料
- 對每一筆資料逐一處理
- 同一個連線中進行多次寫入 (I/O 效能)
示範:
SQL db1 = new SQL(Host1, DBName1, User1, Password1); // 第一個連線
SQL db2 = new SQL(Host2, DBName2, User2, Password2); // 第二個連線
db1.connet(); // 手動連線
db2.connet(); // 手動連線
if (db1.isConn && db2.isConn) // 確認連線成功
{
string sql_1 = "select oid from object"; // 第一個連線,取出資料 SQL
string sql_2 = "insert into TestTable(oid) values(@oid)"; // 第二個連線,寫入資料 SQL
SqlDataReader reader1 = db1.query(sql_1); // 第一個連線,執行查詢
ModelObject[] data1 = db1.fetch<ModelObject>(reader1, false); // 第一個連線,取出一筆資料
while (data1 != null && data1.Length>0) // 第一個連線,有取出資料
{
int id = data1[0].oid;
var para = new
{
oid = id
};
SqlDataReader reader2 = db2.query(sql_2, para); // 第二個連線,執行寫入
var data2 = db2.fetch<ModelObject>(reader2, false); // 第二個連線,取得寫入結果
if (data2 == null) // 寫入失敗
Console.WriteLine("inserted fail");
reader2.Close();
data1 = db1.fetch<ModelObject>(reader1, false); // 第一個連線,取出下一筆資料
}
reader1.Close();
}
// 手動關閉資料庫連線
db1.close();
db2.close();
方法
quickQuery()
與quickExec()
底層實作也同上所示
Transaction
如果需要針對 新增/修改/刪除 的動作添加復原機制,可以開啟 SQL API 內建的 Transaction 功能。
SQL db = new SQL(true);
SQL db = new SQL(Host, DBName, User, Password, true);
示範:
string sql_1 = "insert into TestTable(oid) values(@oid)"; // 第一個 SQL,會執行成功
string sql_2 = "insert2 into TestTable(oid) values(@oid)"; // 第二個 SQL,語法有錯誤,不會執行成功
SQL db = new SQL(true); // 開啟 Transaction
db.connet(); // 連線並開始 Transaction
if (db.isConn)
{
try
{
var reader = db.query(sql_1, new { oid = 10 }); // 執行第一個 SQL
reader.Close();
reader = db.query(sql_2, new { oid = 20 }); // 執行第二個 SQL
reader.Close();
db.tran.Commit(); // 生效所有的語法
}
catch(Exception e)
{
db.tran.Rollback(); // 復原全部的改變
Console.WriteLine(e.ToString()); // 輸出錯誤訊息
}
}
db.close();
輸出:
System.Data.SqlClient.SqlException (0x80131904): 接近關鍵字 'into' 之處的語法不正確。
於 System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
於 System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
於 System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean as
...
Transaction 也支援
quickQuery()
與quickExec()
,但 Stored Procedure 中請勿撰寫 transcation SQL 語法避免衝突
Transaction 在執行過程中會鎖定作用到的所有資料,請注意可能發生效能問題
Writing Large Amounts Of Data
雖然 Detail Control 章節示範了在同一連線中執行多次查詢,但如果要同時寫入超過上千/上萬筆資料,還是會有效能瓶頸。可以使用 SQL API 提供的 Bulk Copy 方法進行大量資料同時寫入
quickBulkCopy(DataTable, TableName)
示範:
// 依照要寫入的 Table 的欄位,建立 DataTable 物件,將要寫入的資料儲存其中
var dt = new DataTable();
dt.Columns.Add("oid", typeof(int));
for(int i =0; i < 100000; i++) // 建立 10 萬筆資料
{
var row = dt.NewRow();
row["oid"] = i;
dt.Rows.Add(row);
}
SQL db = new SQL(); // 建立資料庫存取物件
bool result = db.quickBulkCopy(dt, "dbo.TestTable"); // 指定 Table 名稱,並使用 BulkCopy 方法寫入
if (!result) // 如果寫入失敗
Console.WriteLine(db.getErrorMessage());
根據實測,寫入 10 萬筆資料僅需要 3 秒鐘,完成後,會自動關閉資料庫連線 大量寫入如需對每一筆資料進行額外檢查,可以撰寫 trigger 來達成