熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

Visual Stuido.NET擴展存儲過程

2013-11-13 09:59:34  來源: .NET編程 

  建立數據庫框架
  
  用Sql Sever新建一個數據庫Railway並在Railway中加入兩張表
  
  Routes
  ( TrainID(int) AllStations(varchar())
  )
  
  和
  
  Trains
  (
  TrainID(int)
  TrainName(varchar())
  )
  
  下劃線代表主鍵Trains表中兩項一起作主鍵是為了列車的別名例如T=特快=特等等
  
  最後在Route表中加入如下記錄
  
  ( |北京小時|石家莊小時|鄭州小時|武昌小時|廣州小時|)
  ( |哈爾濱小時|北京小時|石家莊小時|鄭州 小時|西安 小時|成都 小時|)
  
  在Trains表中加入:
  
  ( J)
  ( 極快)
  ( J)
  ( 極快)
  
  Route表中第二項的格式是|起點站小時|下一站離上一站的時間||終點站離上一站的時間|
  
  提出問題
  
  尋找北京到鄭州的最快路線
  尋找哈爾濱到廣州的最快路線
  
  第一個問題很容易看穿因為J和J都經過北京和鄭州只要比較這兩條線路誰更快即可結果應該是J勝出小時此問題不用擴展存儲過程也很容易解決
  
  第二個問題相對就復雜點肯定需要中轉但應該選擇哪個作為中轉站?北京石家莊還是鄭州?這個問題的算法雖然不難但在普通存儲過程裡卻很難編寫
  
  用擴展存儲過程解決第一個問題
  
  在VsNet中建立一個擴展存儲過程項目命名為xsTrainQuery點確定之後在彈出窗口中設置擴展存儲過程名為xs_TrainQuery完成
  
  現在VsNet自動生成了幾個文件如果你是我這種新手就先讀讀Readmetxt裡面對每個文件的作用和加入運行刪除擴展存儲過程xs_TrainQuery的方法有詳細解釋
  
  讓我們把注意力集中到proccpp這個文件的作用是實現xsTrainQuerycpp中定義的接口其內容如下
  
  #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是一個結構在中定義代表一個數據庫客戶端連接的句柄srvh中幾乎所有的方法都要用到這個句柄但這個句柄背後究竟有什麼數據我們完全不用關心(忽然想起寫碩士畢業論文時覺得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首先要在proccpp或者stdafxh中加入#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
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.