一建立數據庫框架 用Sql Sever新建一個數據庫Railway
並在Railway中加入兩張表
Routes
( TrainID(int)
AllStations(varchar(
))
)
和
Trains
(
TrainID(int)
TrainName(varchar(
))
)
下劃線代表主鍵
Trains表中兩項一起作主鍵
是為了列車的別名
例如T
=特快
=特
等等
最後
在Route表中加入如下記錄
(
|北京
小時|石家莊
小時|鄭州
小時|武昌
小時|廣州
小時|
)
(
|哈爾濱
小時|北京
小時|石家莊
小時|鄭州
小時|西安
小時|成都
小時|
)
在Trains表中加入:
(
J
)
(
極快
)
(
J
)
(
極快
)
Route表中第二項的格式是
|起點站
小時|下一站
離上一站的時間|
|終點站
離上一站的時間|
二提出問題 尋找北京到鄭州的最快路線
尋找哈爾濱到廣州的最快路線
第一個問題很容易看穿
因為J
和J
都經過北京和鄭州
只要比較這兩條線路誰更快即可
結果應該是J
勝出
小時
此問題不用擴展存儲過程也很容易解決
第二個問題相對就復雜點
肯定需要中轉
但應該選擇哪個作為中轉站?北京
石家莊還是鄭州?這個問題的算法雖然不難
但在普通存儲過程裡卻很難編寫
三用擴展存儲過程解決第一個問題 在Vs
Net中建立一個擴展存儲過程項目
命名為xsTrainQuery
點確定之後
在彈出窗口中設置擴展存儲過程名為xs_TrainQuery
完成
現在
Vs
Net自動生成了幾個文件
如果你是我這種新手
就先讀讀Readme
txt
裡面對每個文件的作用和加入
運行
刪除擴展存儲過程xs_TrainQuery的方法有詳細解釋
讓我們把注意力集中到proc
cpp
這個文件的作用是實現xsTrainQuery
cpp中定義的接口
其內容如下
#include
// 一些全局常量聲明
#ifdef __cplusplus
extern
C
{
#endif
RETCODE __declspec(dllexport) xs_TrainQuery(SRV_PROC *srvproc); //聲明
#ifdef __cplusplus
}
RETCODE __declspec(dllexport) xs_TrainQuery(SRV_PROC *srvproc) //實現
{
//
}
其中
RETCODE xs_TrainQuery(SRV_PROC*)
正是在數據庫中運行擴展存儲過程xs_TrainQuery後將運行的方法
所以只需要在這一部分中加入適當的代碼
就做出了一個擴展存儲過程
SRV_PROC是一個結構
在中定義
代表一個數據庫客戶端連接的句柄
srv
h中幾乎所有的方法都要用到這個句柄
但這個句柄背後究竟有什麼數據
我們完全不用關心
(忽然想起寫碩士畢業論文時覺得CBitmap類不好用所以自己傻乎乎地超級郁悶地做一個Image類的時候……)
此擴展存儲過程調用的格式應該是xs_TrainQuery [起點站名
終點站名
結果 Output]
那麼解決第一個問題將分為以下幾個步驟
傳入兩個車站名的值
讀入同時含此二車站的所有列車路線
尋找最短的路線
生成結果並傳出之
第一步
讓我們解決傳值問題
擴展存儲過程中的值傳入嚴格來說包括
步
先獲得參數的類型
長度等信息
再設置此參數對應的局部變量的類型和長度
最後讀入此參數
即先調用srv_paraminfo
再alloc一個合適的內存空間
最後調用srv_paraminfo將入參值存入此內存空間
srv_paraminfo的格式為
int srv_paraminfo(
SRV_PROC *srvproc
int n
BYTE *pbType
ULONG *pcbMaxLen
ULONG *pcbActualLen
BYTE *pbData
BOOL *pfNull )
其中
n代表是第幾個參數
比如xs_TrainQuery
北京
成都
中
成都
的n值為
pbType
pcbMaxLen
pcbActualLen分別代表指向此參數的類型
最大長度和實際傳入長度的指針
pbData代表指向此參數值的指針
pfNull代表此參數是否為NULL
若是
則運行srv_paraminfo後
*pfNull將被設置為TRUE
當此參數的信息存在時
srv_paraminfo返回SUCCEED
否則返回FAIL
我們現在要讀入起點和終點站名
就需要如下代碼
{
BOOL bfNull; // 記錄入參是否為空(NULL)
PBYTE pbType; // 入參的類型
ULONG ulMaxLen =
; // 入參的最大長度
令為
字節
ULONG ulActualLen; // 入參的實際長度
PBYTE pbStart; // 起點站名字
PBYTE pbEnd; // 終點站名字
// 獲得第
個參數
即起點站名的類型
長度等信息
if ( srv_paraminfo(srvproc
pbType
&ulMaxLen
&ulActualLen
NULL
&bfNULL) !=
SUCCEED )
{
// 一些異常處理代碼
}
// 為起點站入參分配空間
pbStart = (PBYTE)::malloc(ulActualLen);
if ( pbStart == NULL)
{
// 一些異常處理代碼
}
// 獲得第
個參數的值
if ( srv_paraminfo(srvproc
pbType
&ulMaxLen
&ulActualLen
pbStart
&bfNULL)
!= SUCCEED )
{
// 一些異常處理代碼
}
// 重復上面的三步
但n變為
pbStart變為pbEnd
讀入終點站名
}
第二步
從數據庫中取得含二車站名的所有路線
在擴展存儲過程中連接數據庫有兩種方法
第一種請參見MS Sql Server ODS示例中的xp_dblib
這裡只介紹第二種方法
采用SQL開頭的一系列ODBC API
為了使用這些API
首先要在proc
cpp或者stdafx
h中加入#include
一次數據庫連接的過程包括以下幾個步驟
初始化ODBC連接
並分配環境句柄
設置環境
根據環境句柄分配數據庫連接句柄
調用SQLDriverConnect連接MS SQL Serve數據源
分配和使用語句
調用SQLDisconnect斷開連接
依次釋放分配的句柄
直接用例子來說明
現在我們要從數據庫中讀取含此二車站名的所有路線
可以用下面的代碼
{
SQLHENV hEnv = SQL_NULL_ENV; // 環境句柄
SQLHDBC hDbc = SQL_NULL_HDBC; // 連接句柄
SQLHSTMT hStmt = SQL_NULL_HSTMT; // 語句句柄
SQLCHAR connStr[
] =
Driver={SQL Server};SERVER=localhost;UID=你的用戶名;PWD=你的密碼;DATABASE=Railway;
;
// 分配環境句柄
SQLAllocHandle(SQL_HANDLE_ENV
NULL
&hEnv);
// 設置連接環境
ODBC版本設為
x
SQLSetEnvAttr(hEnv
SQL_ATTR_ODBC_VERSION
(SQLPOINTER)SQL_OV_ODBC
SQL_IS_INTEGER);
// 根據環境分配連接句柄
SQLAllocHandle(SQL_HANDLE_DBC
hEnv
&hDbc);
// 建立數據庫連接
SQLDriverConnect(hDbc
NULL
connStr
SQL_NTS
NULL
NULL
SQL_DRIVER_NOPROMPT);
// 根據連接分配語句句柄
SQLAllocHandle(SQL_HANDLE_STMT
hDbc
&hStmt);
// 定義查詢語句query
別忘記#include
TCHAR query[] = select RoutesTrainID AllStations from Routes Trains where RoutesTrainID=TrainsTrainID and AllStations like %;
_tcscat(query (TCHAR*)pbStart);
_tcscat(query %);
_tcscat(query (TCHAR*)pbEnd);
_tcscat(query %);
// 現在query=select Routes…… like %起點站%終點站%我們運行此查詢
SQLExecDirect(hStmt (SQLCHAR*)query SQL_NTS);
// 獲得查詢所得的行數
UINT uiRowsCount; //結果集中的行數
SQLSetStmtAttr(hStmt SQL_ATTR_ROWS_FETCHED_PTR) (void*)&uiRowsCount sizeof(SQLINTEGER));
// 初始化結果集數組包括兩個TrainID和AllStations
int *piTrainID = (int*)::malloc(uiRowsCount * sizeof(int));
PCHAR *ppcStations = (PCHAR*)::malloc(uiRowsCount * sizeof(PCHAR));
// 因為結果集中第一列即TrainID為整型長度不變故可以直接綁定
SQLBindCol(hStmt SQL_INTEGER (SQLPOINTER)piTrainID sizeof(int) NULL);
// 取出第二列的數據因為其長度不定故必須先獲得長度
SQLINTEGER lColLen; // 字符串的列即AllStations長度
UINT nRow = ; // 行標
while (SQLFetch(hStmt) == SQL_SUCCESS_WITH_INFO) //取出一行
{
// 獲得長度
SQLGetData(hStmt SQL_CHAR NULL &lColLen);
// 根據長度為第二列的數據分配空間
ppcStation[nRow] = ::malloc(lColLen);
// 獲得數據
SQLGetData(hStmt SQL_CHAR ppcStation[nRow] lColLen &lColLen);
// 行標隨行的Fetch遞增
++nRow;
}
// 現在我們就取出了所有TrainID和AllStations
// 關閉數據庫連接釋放資源
SQLFreeHandle(SQL_HANDLE_STMT hStmt);
SQLDisConnect(hDbc);
SQLFreeHandle(SQL_HANDLE_DBC hDbc);
SQLFreeHandle(SQL_HANDLE_ENV hEnv);
From:http://tw.wingwit.com/Article/program/net/201311/12221.html