在VC++中用ODBC訪問SQL Server資料庫
ODBC(Open Database Connectivity,開放資料庫連線)是由Microsoft定義的一種資料庫訪問標準,它提供了一種標準的資料庫訪問方法以訪問不同資料庫提供商的資料庫,其本質上是一組資料庫訪問API.雖然資料庫訪問有多種方法,但ODBC以其程式設計相對簡單,在實際程式設計中被廣泛使用。
VC++中提供了一組封裝了ODBC API的MFC ODBC類,以減少程式程式碼編寫量。在VC++中使用MFC ODBC類訪問資料庫時,一般都是先配置或選擇已有的資料來源,再構造CDatabase類物件,開啟資料來源,用該資料庫類物件和SQL(結構化查詢語言)可以對庫進行訪問,再構造CRecordset類或其繼承類物件,用資料集類物件和SQL可以實現對庫中的表的操作。在VC++中用MFC ODBC類操作SQL Server資料庫基本上也是這個思路。
1.配置資料來源
在程式中根據使用者選擇動態配置資料來源而不呼叫ODBC資料來源管理器,對於應用程式開發有時是十分必要的。畢竟ODBC資料來源管理器對於對資料庫不熟悉的使用者顯的複雜了,且ODBC資料來源管理器的介面風格有可能與整個應用程式的介面風格不一致,對於嚴謹的應用程式開發者來說,這些都是不能容忍的。
配置SQL Server資料來源時,必須有SQL Server伺服器名和伺服器中的目標資料庫名(否則開啟該資料來源時,就會彈出配置資料來源對話方塊或失敗)。這與配置Access等資料庫的資料來源時指定資料庫檔案的路徑不同。
可以通過ODBC API函式SQLBrowseConnect得到本地所有的SQL Server伺服器、伺服器中的庫、語言資訊等。其使用方法如下所示:
①得到伺服器名
SQLBrowseConnect(hdbc, "DRIVER={SQL Server};", SQL_NTS, BrowseResult, sizeof(BrowseResult), &BrowseResultLen);
其中hdbc是用SQLAllocHandle函式得到的連結控制代碼,第二個引數是指定連結屬性的輸入字串,第三個引數是輸入字串的長度,第四個引數是輸出字串的指標,我們要得到的資訊就存於這個字串中,第五個引數指定輸出字串的長度,第六個引數是實際返回字串的長度。BrowseResult所指向的函式返回的字串中含有形如“SERVER:Server={Server_name1,Server_name2,…}”的字串,其中Server_name1、Server_name2就是SQL Server伺服器名。
②得到庫名
當用戶選定一個伺服器之後,可以進一步利用SQLBrowseConnect函式得到伺服器中存在的庫或語言資訊(如果需要的話)。
SQLBrowseConnect(hdbc,"SERVER=Server_name1;UID=sa;PWD=515578;",SQL_NTS, BrowseResult, sizeof(BrowseResult), &BrowseResultLen);
其中UID和PWD是訪問該伺服器的使用者名稱和密碼。這次BrowseResult所指向的函式返回的字串中含有形如DATABASE:Database={master,model,…}和LANGUAGE:Language={Arabic, Brazilian, English, …}的字串,其中master、model為伺服器中的資料庫名。
利用這些得到資訊就可以配置SQL Server資料來源了:
SQLConfigDataSource(NULL,ODBC_ADD_DSN,"SQL Server","DSN=myDSN\0 SERVER=xhm\0DATABASE=pubs\0\0" ))
作者有幸在網上找到Santosh Rao編寫的CSQLInfoEnumerator類,該類有 EnumerateSQLServers、EnumerateDatabase、EnumerateDatabaseLanguage三個成員函式,其封裝了SQLBrowseConnect函式並進行相應的字元處理,大大簡化了SQLBrowseConnect函式的使用。
2.與資料來源建立連線
如果使用ODBC API函式進行連結,函式SQLConnect、SQLDriverConnect和SQLBrowseConnect都可以實現。其中SQLConnect函式用於給定所有引數直接建立與資料來源的連線;SQLDriverConnect用於給定了部分連線引數,並彈出資料來源瀏覽視窗與使用者互動,獲得足夠的引數後建立與資料來源的連線;而SQLBrowseConnect是通過迭代獲取連結引數再進行連線,其使用如前所述。
MFC為連線資料來源提供了一個數據庫類CDatabase,通過它我們可以非常方便地與資料來源建立連結: //m_db是CDatabase的物件
//m_szUserId,m_szPassword是CString物件,為訪問資料來源的使用者名稱和密碼
CString str;
str = _T("DSN=xhmtest;UID=")+m_szUserId+_T(";PWD=")+m_szPassword;
if (! m_db.OpenEx(str, CDatabase::noOdbcDialog ) ){
AfxMessageBox("開啟資料來源失敗");
}
另外,如果我們想對資料來源進行操作,就可以利用開啟的CDatabase物件執行SQL語句實現。如新建一張表: str = "CREATE TABLE TableDemo (Column1TEXT, Column2 NUMBER)";
m_db.ExecuteSQL(sSql);
3.得到資料庫的結構資訊
在VC++中可以單獨利用ODBC API獲取資料庫的結構資訊,但方法非常複雜,各種引數也不易理解,且返回的結果不易獲取。MFC中也沒有為應用程式開發者得到資料庫結構資訊而提供單獨的類。但是我們還是可以通過一些已有的方法方便地獲得資料庫的結構資訊。
3.1 得到資料庫中的表
利用ODBC API函式SQLTables可以得到資料庫中的表資訊,但使用不易。
在MSDN的sample中有一個catalog例程,它示範瞭如果得到一個數據庫的表資訊和表中的欄位資訊,該程式中有兩個非常實用的類:Ctables和Ccolumns.其中CTables繼承CRecordset類,通過非常巧妙的方法封裝了SQLTables函式,並將結果集存於CRecordset類,方便獲取。
在程式中我們可以這樣應用: //m_db是已經開啟的CDatabse物件,
//m_strTableOwner是CComboBox物件
CTables rs(&m_db);
rs.Open(NULL, NULL, NULL, "TABLE");
CString strTableRef;
m_combTable.ResetContent();
while (!rs.IsEOF())
{
strTableRef = _T("[");
if (!rs.m_strTableOwner.IsEmpty())
strTableRef += rs.m_strTableOwner + _T("].[");
strTableRef += rs.m_strTableName + _T("]");
m_combTable.AddString(strTableRef);
rs.MoveNext();
}
rs.Close();
在CTables物件Open方法第四個能數指定“TABLE”是指返回庫中的表,當該引數為“SYSTEM TABLE”時返回系統表,當為NULL時,返回所有表。
3.2 得到表中欄位結構
得到表中欄位結構有三種具體實現方式:
①直接利用ODBC API 函式SQLColumn;其實現也同樣複雜。
②利用前面提到的封裝了SQLColumns函式的CColumns類;其使用方法與CTables類似,其成員變數m_strColumnName即為欄位名
③利用CRecordset類的成員函式。MFC在CRecordset類中提供了獲取表結構資訊方法,使用非常方便。(但很奇怪為什麼CDatabase類沒有獲取庫結構資訊的類方法?)
我們以利用CRecodrset為例簡單說明怎樣得到表結構。假設現在要得到使用者在CComboBox物件中所選表的所有欄位名,並將它埴入一個CListCtrl物件(其View屬性被設為Report)的列名中去: //m_strTableOwner和上同,為CComboBox物件
//m_listData為ClistCtrl物件
CRecordset rs
CODBCFieldInfo info;
CString strSQL;
m_combTable.GetLBText(m_combTable.GetCurSel(), strSQL);
strSQL = _T("SELECT * FROM ") + strSQL;
rs.Open(Open(CRecordset::snapshot, strSQL, CRecordset::readOnly);
int nColumns = rs.GetODBCFieldCount();
for (int nNum = 0; nNum < nColumns; nNum++)
{
prs->GetODBCFieldInfo(nNum, info);
m_listData.InsertColumn(nNum, info.m_strName, LVCFMT_LEFT, 80)
}
另外,我們可以利用CRecordSet::GetFieldValue函式通過指定列名或列序數得到記錄集中游標所指記錄的值,通過CRecordset::AddNew、CRecordset::CancelUpdate、CRecordset::Delete、CRecordset::Edit、CRecordset::Update等函式操作CRecordset開啟的表。