ADO中最重要的对象有三个:Connection、Recordset和Command,分别表示连接对象、记录集对象和命令对象。三个对象对应的智能指针分别是:_ConnectionPtr、_RecordsetPtr、_CommandPtr。ADO使用_ConnectionPtr这个指针来操纵Connection对象,类似地,后面用到的_CommandPtr和_RecordsetPtr分别表示命令对象指针和记录集对象指针。
Connection对象是这三个对象的基础,它的主要作用是建立与数据库的连接,建立了与数据库的连接后,才能进行其它有关数据库的访问和操作。
也就是说,使用ADO操作数据库,通常先用Connection对象的Open方法打开一个库连接,然后才能进行数据库的操作。操作完成后,要关闭这个库连接。
本文只讲述Connection对象最常用的Open方法和Execute方法。Open方法用于打开一个库连接,而Execute方法一般用于执行一条SQL语句。
_ConnectionPtr智能指针的用法:
首先定义一个Connection类型的指针,然后调用CreateInstance()来创建一个连接对象的实例,再调用Open函数建立与数据源的连接。在建立连接对象后,可以使用连接对象的Execute()函数来执行SQL命令。
_ConnectionPtr智能指针Open方法的原型:
Open(_bstr_t ConnectionString,_bstr_t UserID,_bstr_t Password,long Options)
ConnectionString为连接字串,UserID是用户名,Password是登陆密码
Options是连接选项,可以是如下几个常量:
adModeUnknown 缺省,当前的许可权未设置
adModeRead 只读
adModeWrite 只写
adModeReadWrite 可以读写
adModeShareDenyRead 阻止其它Connection对象以读权限打开连接
adModeShareDenyWrite 阻止其它Connection对象以写权限打开连接
adModeShareExclusive 阻止其它Connection对象打开连接
adModeShareDenyNone 阻止其它程序或对象以任何权限建立连接
_ConnectionPtr智能指针Execute方法的原型:
_RecordsetPtr Connection15::Execute(_bstr_t CommandText,VARIANT* RecordsAffected,long Options)
其中CommandText是命令字串,通常是SQL命令,
参数RecordsAffected是操作完成后所影响的行数
参数Options表示CommandText中内容的类型,可以取下列值之一:
adCmdText 表明CommandText是文本命令
adCmdTable 表明CommandText是一个表名
adCmdProc 表明CommandText是一个存储过程
adCmdUnknown 未知
Execute执行完后返回一个指向记录集的指针。
例程CREATE_DB_AND_TABLE中已经使用了_ConnectionPtr指针的Open方法和Execute方法,在后面的例程我们将进一步详细说明。
我们先讲解几条最常用的SQL语句。
SELECT查询语句
我们希望用各种不同的方法来查看和分析数据,SELECT语句就是我们要使用的语句,用于有选择的从数据库返回我们需要的数据,也就是查询。
最基本的SELECT语句仅有两个部分:要返回的列和这些列源于的表
为了便于讲解演示,我们使用如下Northwind 示例数据库中的 Employees 表
EmployeeID |
FirstName |
LastName |
HireDate |
City |
Country |
1 |
Nancy |
Davolio |
1/5/1992 12:00:00 |
Seattle |
USA |
2 |
Andrew |
Fuller |
14/8/1992 12:00:00 |
Tacoma |
USA |
3 |
Janet |
Leverling |
1/4/1992 12:00:00 |
Kirkland |
USA |
4 |
Margaret |
Peacock |
3/5/1993 12:00:00 |
Redmond |
USA |
5 |
Steven |
Buchanan |
17/10/1993 12:00:00 |
London |
UK |
6 |
Michael |
Suyama |
17/10/1993 12:00:00 |
London |
UK |
7 |
Robert |
King |
2/1/1994 12:00:00 |
London |
UK |
8 |
Laura |
Callahan |
5/3/1994 12:00:00 |
Seattle |
USA |
9 |
Anne |
Dodsworth |
15/11/1994 12:00:00 |
London |
UK |
如果我们希望检索Employees表中所有客户的所有信息,我们可以使用星号(*)来简单地表示所有列,查询语句如下所示:
SELECT * FROM Employees
如果我们只需要特定列,我们应该在逗号分隔的列表中显式指定这些列,如下所示:
SELECT EmployeeID, FirstName,LastName,HireDate FROM Employees
结果会显示该表中所有行的指定字段的数据。
显式指定所需字段还允许我们控制字段返回的顺序,如果我们希望LastName显示在FirstName之前,我们可以编写以下语句:
SELECT EmployeeID, LastName,FirstName,HireDate FROM Employees
WHERE子句
接下来我们要做的是开始限制或筛选从数据库提取的数据。通过向SELECT语句添加WHERE子句,我们可以添加一个(或多个)条件,所选数据必须满足这些条件,这将限制答复查询的行数也就是被提取的行数。
我们可以在上一个查询的基础上,将其限制为City为London的员工
SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees
WHERE City = 'London'
查询结果如下:
EmployeeID |
FirstName |
LastName |
HireDate |
City |
5 |
Steven |
Buchanan |
17/10/1993 12:00:00 |
London |
6 |
Michael |
Suyama |
17/10/1993 12:00:00 |
London |
7 |
Robert |
King |
2/1/1994 12:00:00 |
London |
9 |
Anne |
Dodsworth |
15/11/1994 12:00:00 |
London |
如果您希望返回相反条件的员工,即返回那些不住在伦敦的员工,您应该编写以下语句:
SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees
WHERE City <> 'London'
您也可以使用大于、小于、大于等于、小于等于等运算符。例如,若要获取其雇佣日期等于某个给定日期或大于该日期的员工列表,您可以编写以下语句:
SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees
WHERE HireDate >= '1-july-1993'
您可以得到以下结果行:
EmployeeID |
FirstName |
LastName |
HireDate |
City |
5 |
Steven |
Buchanan |
17/10/1993 12:00:00 |
London |
6 |
Michael |
Suyama |
17/10/1993 12:00:00 |
London |
7 |
Robert |
King |
2/1/1994 12:00:00 |
London |
8 |
Laura |
Callahan |
5/3/1994 12:00:00 |
Seattle |
9 |
Anne |
Dodsworth |
15/11/1994 12:00:00 |
London |
当然,我们可以编写更复杂的条件:在 WHERE 子句中加入多个条件。如果我们希望了解哪些员工是在两个给定日期之间雇佣的,我们可以编写以下语句:
SELECT EmployeeID, FirstName, LastName, HireDate, City
FROM Employees
WHERE (HireDate >= '1-june-1992') AND (HireDate <= '15-december-1993')
结果如下:
EmployeeID |
FirstName |
LastName |
HireDate |
City |
2 |
Andrew |
Fuller |
14/8/1992 12:00:00 |
Tacoma |
4 |
Margaret |
Peacock |
3/5/1993 12:00:00 |
Redmond |
5 |
Steven |
Buchanan |
17/10/1993 12:00:00 |
London |
6 |
Michael |
Suyama |
17/10/1993 12:00:00 |
London |
SQL 还有一个BETWEEN 运算符,用于检查某个值是否在两个值之间(包括等于两端的值)。这使我们可以将以前的查询重新编写为:
SELECT EmployeeID, FirstName, LastName, HireDate, City
FROM Employees
WHERE HireDate BETWEEN '1-june-1992' AND '15-december-1993'
我们也可以使用 NOT 运算符来提取那些不在指定日期之间的行:
SELECT EmployeeID, FirstName, LastName, HireDate, City
FROM Employees
WHERE HireDate NOT BETWEEN '1-june-1992' AND '15-december-1993'
如果我们希望检查一个列值是否等于多个值,那该怎么办呢?如果只是两个值,则很容易对每个值进行测试,方法是,使用 OR 运算符将它们连接在一起,则编写的语句如下所示:
SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees
WHERE City = 'London' OR City = 'Seattle'
但是,如果您希望与三个、四个或更多值进行比较,则上述方法就行不通了。在这种情况下,我们可以使用 IN 运算符来对一组值进行测试。如果我们希望查看 City 是否为 Seattle、Tacoma 或 Redmond,我们可以编写以下语句:
SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees
WHERE City IN ('Seattle', 'Tacoma', 'Redmond')
显示以下结果:
EmployeeID |
FirstName |
LastName |
HireDate |
City |
1 |
Nancy |
Davolio |
1/5/1992 12:00:00 |
Seattle |
2 |
Andrew |
Fuller |
14/8/1992 12:00:00 |
Tacoma |
4 |
Margaret |
Peacock |
3/5/1993 12:00:00 |
Redmond |
8 |
Laura |
Callahan |
5/3/1994 12:00:00 |
Seattle |
我们也可以获得所含结果的相反条件下的结果,即查询那些 City 不在指定列表中的行:
SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees
WHERE City NOT IN ('Seattle', 'Tacoma', 'Redmond')
最后要说明的是,LIKE 运算使我们可以使用通配符来执行基本的模式匹配。在 Microsoft SQL Server 中,定义的通配符包含以下字符:
通配符 |
说明 |
_(下划线) |
与任意单字符匹配 |
% |
与包含一个或多个字符的字符串匹配 |
[ ] |
与特定范围(例如,[a-f])或特定集(例如,[abcdef])中的任意单字符匹配。 |
[^] |
与特定范围(例如,[^a-f])或特定集(例如,[^abcdef])之外的任意单字符匹配。 |
以下一些示例可以帮助阐明上述规则。
WHERE FirstName LIKE '_im' 可以找到所有三个字母的、以 im 结尾的名字(例如,Jim、Tim)。 |
WHERE LastName LIKE '%stein' 可以找到姓以 stein 结尾的所有员工。 |
WHERE LastName LIKE '%stein%' 可以找到姓中任意位置包括 stein 的所有员工。 |
WHERE FirstName LIKE '[JT]im' 可以找到三个字母的、以 im 结尾并以 J 或 T 开始的名字(即仅有 Jim 和 Tim) |
WHERE LastName LIKE 'm[^c]%' 可以找到以 m 开始的、后面的(第二个)字母不为 c 的所有姓。 |
此处我们也选择使用 NOT 运算符:若要找到所有名字不以 M 或 A 开始的员工,我们可以编写以下语句:
SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees
WHERE (FirstName NOT LIKE 'M%') AND (FirstName NOT LIKE 'A%')
结果如下:
EmployeeID |
FirstName |
LastName |
HireDate |
City |
1 |
Nancy |
Davolio |
1/5/1992 12:00:00 |
Seattle |
3 |
Janet |
Leverling |
1/4/1992 12:00:00 |
Kirkland |
5 |
Steven |
Buchanan |
17/10/1993 12:00:00 |
London |
7 |
Robert |
King |
2/1/1994 12:00:00 |
London |
8 |
Laura |
Callahan |
5/3/1994 12:00:00 |
Seattle |
ORDER BY 子句
直到现在我们一直在讨论筛选数据:即定义一些条件,来确定哪些行将包括在从数据库提取并返回的最终行集中。一旦我们确定了哪些列和行将包括在 SELECT 查询的结果中,我们可能就希望控制这些行显示的顺序:对数据排序。
若要对数据行排序,我们需要 ORDER BY 子句。ORDER BY 子句包括了一个或多个用于指定排序顺序的列名。如果返回至第一批 SELECT 语句中的某条语句,我们可以用以下语句按 City 对其结果排序:
SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees
ORDER BY City
默认情况下,列的排序顺序为升序(从最小值到最大值),上述查询的结果如下所示:
EmployeeID |
FirstName |
LastName |
HireDate |
City |
3 |
Janet |
Leverling |
1/4/1992 12:00:00 |
Kirkland |
5 |
Steven |
Buchanan |
17/10/1993 12:00:00 |
London |
6 |
Michael |
Suyama |
17/10/1993 12:00:00 |
London |
7 |
Robert |
King |
2/1/1994 12:00:00 |
London |
9 |
Anne |
Dodsworth |
15/11/1994 12:00:00 |
London |
4 |
Margaret |
Peacock |
3/5/1993 12:00:00 |
Redmond |
1 |
Nancy |
Davolio |
1/5/1992 12:00:00 |
Seattle |
8 |
Laura |
Callahan |
5/3/1994 12:00:00 |
Seattle |
2 |
Andrew |
Fuller |
14/8/1992 12:00:00 |
Tacoma |
如果我们希望列的排序顺序为降序,我们可以在列名后包括 DESC 关键字。
ORDER BY 子句支持使用多列。您可以包括以逗号分隔的多个列以按其排序:行将先按指定的第一列进行排序,然后再按指定的下一列进行排序。如果我们向 SELECT 子句添加 Country 字段并希望按 Country 和 City 排序,我们可以编写以下语句:
SELECT EmployeeID, FirstName, LastName, HireDate, Country, City FROM Employees ORDER BY Country, City DESC
请注意,为了使语句显得更有趣,我们已经指定了 City 列的排序顺序为降序(从最大值到最小值)。Country 列的排序顺序仍为升序。为了更清楚地了解到这一点,我们可以把语句编写为:
SELECT EmployeeID, FirstName, LastName, HireDate, Country, City FROM Employees ORDER BY Country ASC, City DESC
但是,默认情况下,列的排序顺序为升序。所以,加上ASC不是必要的,并且很少这么做。此查询返回的结果如下所示:
EmployeeID |
FirstName |
LastName |
HireDate |
Country |
City |
5 |
Steven |
Buchanan |
17/10/1993 12:00:00 |
UK |
London |
6 |
Michael |
Suyama |
17/10/1993 12:00:00 |
UK |
London |
7 |
Robert |
King |
2/1/1994 12:00:00 |
UK |
London |
9 |
Anne |
Dodsworth |
15/11/1994 12:00:00 |
UK |
London |
2 |
Andrew |
Fuller |
14/8/1992 12:00:00 |
USA |
Tacoma |
1 |
Nancy |
Davolio |
1/5/1992 12:00:00 |
USA |
Seattle |
8 |
Laura |
Callahan |
5/3/1994 12:00:00 |
USA |
Seattle |
4 |
Margaret |
Peacock |
3/5/1993 12:00:00 |
USA |
Redmond |
3 |
Janet |
Leverling |
1/4/1992 12:00:00 |
USA |
Kirkland |
重要的是要注意要在 ORDER BY 子句中使用某一列时,并不需要在选定的(返回的)多个列中包括这一列。如果我们不需要查看Country 值,只是对于将其作为主排序字段感兴趣,可以编写以下查询:
SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees
ORDER BY Country ASC, City DESC
所得结果的排序顺序与上一个查询所得结果的排序顺序相同:
EmployeeID |
FirstName |
LastName |
HireDate |
City |
5 |
Steven |
Buchanan |
17/10/1993 12:00:00 |
London |
6 |
Michael |
Suyama |
17/10/1993 12:00:00 |
London |
7 |
Robert |
King |
2/1/1994 12:00:00 |
London |
9 |
Anne |
Dodsworth |
15/11/1994 12:00:00 |
London |
2 |
Andrew |
Fuller |
14/8/1992 12:00:00 |
Tacoma |
1 |
Nancy |
Davolio |
1/5/1992 12:00:00 |
Seattle |
8 |
Laura |
Callahan |
5/3/1994 12:00:00 |
Seattle |
4 |
Margaret |
Peacock |
3/5/1993 12:00:00 |
Redmond |
3 |
Janet |
Leverling |
1/4/1992 12:00:00 |
Kirkland |
INSERT INTO语句
INSERT INTO语句用于新增一个记录。该语句的格式:
INSERT INTO 表 [(字段1[,字段2[, ...]])] VALUES (值1[,值2[, ...])
新增的记录,将依照指定字段排列的顺序插入对应的值,譬如,值1将被插入至字段1,值2将被插入至字段2,依此类推。
若未指定 (字段1[,字段2[, ...]]) ,将依照表所定义的字段排列的顺序插入对应的值,在VALUES 子句必须包含表中的每一字段值。
值间须使用逗点分隔,文字字段须使用单用引号 ('值') 括起来。
例如,向Employees表中插入一条记录。
INSERT INTO Employees (EmployeeID, FirstName, LastName,HireDate ,City , Country) VALUES (10, ‘Mary’ ‘Williams’,’15/4/1993 12:00:00’,’New York’,’USA’)
Update语句
用途:更新表中原有数据
语法:
Update table_name Set column_name = new_value Where column_name = some_value
例:
把EmployeeID为10的记录改为:FirstName是Bill,LastName是Clinton,HireDate是25/11/1994 12:00:00,City是Los Angeles,Country不变,还是USA
UPDATE Employees SET FirstName = 'Bill', LastName=’Clinton’, HireDate=’25/11/1994 12:00:00’,City = 'Los Angeles' WHERE EmployeeID=10
好了,SQL命令我们先介绍这几条,下面,我们通过例程ConnPtr_Open_Exe演示_ConnectionPtr的Open函数建立与数据源的连接并使用Execute()函数来执行SQL命令。
例程ConnPtr_Open_Exe
打开VC++ 6.0,新建一个基于对话框的工程ConnPtr_Open_Exe。在对话框IDD_CONNPTR_OPEN_EXE_DIALOG中进行编辑:
使用三个Group Box分成三个部分,第一部分演示使用Execute()函数来执行INSERT INTO命令;第二部分演示使用Execute()函数来执行Update命令;第三部分演示使用Execute()函数来执行SELECT命令。其中,第一部分和第二部分不需要返回记录集,第三部分演示返回记录集显示结果。
该对话框几个控件如下:
控件名称 |
ID |
用途 |
按钮 |
IDC_BTN_INSERT_INTO |
执行INSERT INTO语句 |
按钮 |
IDC_BTN_UPDATE |
执行Update语句 |
按钮 |
IDC_BTN_SELECT |
执行SELECT语句 |
列表框 |
IDC_LIST1 |
显示SELECT语句执行结果 |
使用ClassWizard给列表框IDC_LIST1创建CListBox变量m_list1:
双击IDC_BTN_INSERT_INTO按钮,并编辑OnBtnInsertInto()函数如下:
void CConnPtr_Open_ExeDlg::OnBtnInsertInto()
{
_ConnectionPtr m_pConnection;
_variant_t RecordsAffected;
try
{
m_pConnection.CreateInstance(__uuidof(Connection));
m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Northwind.mdb","","",adModeUnknown);
}
catch(_com_error e)
{
CString errormessage;
errormessage.Format("连接数据库失败!/r错误信息:%s",e.ErrorMessage());
AfxMessageBox(errormessage);
return;
}
try
{
_bstr_t strCmd="INSERT INTO Employees(EmployeeID,FirstName,LastName,HireDate,City,Country) VALUES(10,'Mary','Williams','15/4/1993 12:00:00','New York','USA')";
m_pConnection->Execute(strCmd,&RecordsAffected,adCmdText);
}
catch(_com_error &e)
{
AfxMessageBox(e.Description());
}
if(m_pConnection->State)
m_pConnection->Close();
}
双击IDC_BTN_UPDATE按钮,并编辑OnBtnUpdate()函数如下:
void CConnPtr_Open_ExeDlg::OnBtnUpdate()
{
_ConnectionPtr m_pConnection;
_variant_t RecordsAffected;
try
{
m_pConnection.CreateInstance(__uuidof(Connection));
m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Northwind.mdb","","",adModeUnknown);
}
catch(_com_error e)
{
CString errormessage;
errormessage.Format("连接数据库失败!/r错误信息:%s",e.ErrorMessage());
AfxMessageBox(errormessage);
return;
}
try
{
_bstr_t strCmd="UPDATE Employees SET FirstName='Bill',LastName='Clinton',HireDate='25/11/1994 12:00:00',City='Los Angeles' WHERE EmployeeID=10";
m_pConnection->Execute(strCmd,&RecordsAffected,adCmdText);
}
catch(_com_error &e)
{
AfxMessageBox(e.Description());
}
if(m_pConnection->State)
m_pConnection->Close();
}
双击IDC_BTN_SELECT按钮,并编辑OnBtnSelect()函数如下:
void CConnPtr_Open_ExeDlg::OnBtnSelect()
{
_ConnectionPtr m_pConnection;
_variant_t RecordsAffected;
_RecordsetPtr m_pRecordset;
try
{
m_pConnection.CreateInstance(__uuidof(Connection));
m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Northwind.mdb","","",adModeUnknown);
}
catch(_com_error e)
{
CString errormessage;
errormessage.Format("连接数据库失败!/r错误信息:%s",e.ErrorMessage());
AfxMessageBox(errormessage);
return;
}
try
{
m_pRecordset.CreateInstance("ADODB.Recordset"); //为Recordset对象创建实例
_bstr_t strCmd="SELECT EmployeeID,FirstName,LastName,HireDate,City FROM Employees WHERE City='London'";
m_pRecordset=m_pConnection->Execute(strCmd,&RecordsAffected,adCmdText);
}
catch(_com_error &e)
{
AfxMessageBox(e.Description());
}
_variant_t vEmployeeID,vFirstName,vLastName,vHireDate,vCity;
try
{
while(!m_pRecordset->adoEOF)
{
vEmployeeID=m_pRecordset->GetCollect(_variant_t((long)0));
//取得第1列的值,从0开始计数,你也可以直接列出列的名称,如下一行
vFirstName=m_pRecordset->GetCollect("FirstName");
vLastName=m_pRecordset->GetCollect("LastName");
vHireDate=m_pRecordset->GetCollect("HireDate");
vCity=m_pRecordset->GetCollect("City");
CString strtemp;
if(vEmployeeID.vt!=VT_NULL)
{
strtemp.Format("%d",vEmployeeID.lVal);
}
if(vFirstName.vt!=VT_NULL)
{
strtemp+=" ";
strtemp+=(LPCTSTR)(_bstr_t)vFirstName;
}
if(vLastName.vt!=VT_NULL)
{
strtemp+=" ";
strtemp+=(LPCTSTR)(_bstr_t)vLastName;
}
if(vHireDate.vt!=VT_NULL)
{
strtemp+=" ";
strtemp+=(LPCTSTR)(_bstr_t)vHireDate;
}
if(vCity.vt!=VT_NULL)
{
strtemp+=" ";
strtemp+=(LPCTSTR)(_bstr_t)vCity;
}
m_list1.AddString(strtemp);
m_list1.AddString("/n");
m_pRecordset->MoveNext();
}
}
catch(_com_error &e)
{
AfxMessageBox(e.Description());
}
m_pRecordset->Close();
m_pRecordset=NULL;
m_pConnection->Close();
m_pConnection=NULL;
}
在stdafx.h中加入如下语句:
#import "C:/Program Files/Common Files/system/ado/msado15.dll" rename("EOF","adoEOF")
在BOOL CConnPtr_Open_ExeApp::InitInstance()函数中加入:
AfxOleInit();
编译并运行该程序,观察运行结果。点击IDC_BTN_INSERT_INTO按钮,打开数据库Northwind.mdb的Employees表,你就会发现增加了一条记录:
10, Mary , Williams , 15/4/1993 12:00:00 , New York , USA
关闭数据库。
继续点击IDC_BTN_UPDATE按钮,打开数据库Northwind.mdb的Employees表,你就会发现第10条记录变为:
10, Bill , Clinton , 25/11/1994 12:00:00 , Los Angeles, USA
关闭数据库。
继续点击IDC_BTN_SELECT按钮,你就会发现列表框中会显示出City为London的记录,如下:
EmployeeID |
FirstName |
LastName |
HireDate |
City |
5 |
Steven |
Buchanan |
17/10/1993 12:00:00 |
London |
6 |
Michael |
Suyama |
17/10/1993 12:00:00 |
London |
7 |
Robert |
King |
2/1/1994 12:00:00 |
London |
9 |
Anne |
Dodsworth |
15/11/1994 12:00:00 |
London |
该部分演示了如何使用_ConnectionPtr接口开发ACCESS数据库:先创建一个Connection对象实例,然后用Open方法打开一个库连接,最后使用Execute方法执行SQL语句进行其它有关数据库的访问和操作。接下来的第五部分将演示使用使用_RecordsetPtr接口开发ACCESS数据库。