VC++使用ADO开发ACCESS数据库
ADO和ADOX到底是什么,二者的作用和区别
ADO是Microsoft 最新推出的数据库访问的高层软件接口。它和Microsoft以前的数据库访问接口DAO、RDO相比具有更大的灵活性,使用也更方便,开发效率大为提高。
ADOX是核心ADO对象的扩展库。它提供的附加对象可用于创建、修改和删除模式对象,如表和过程。要使用ADOX,则应建立对ADOX类型库的引用。ADOX库文件名为Msadox.dll。
通俗地讲,ADO是访问数据库的一种接口,可以使用它方便地进行数据库编程。而ADOX是微软对ADO功能的扩展,比如:可以ADOX创建数据库(而ADO没有创建数据库的功能)。
ADOX创建ACCESS数据库
用ADOX创建access数据库方法很简单,只需要创建一个Catalog对象,然后调用它的Create方法就可以了。
例程ADOXCreateDatabase演示如何使用ADOX创建一个ACCESS数据库。
打开VC++ 6.0,新建一个基于对话框的工程ADOXCreateDatabase。在对话框IDD_ADOXCREATEDATABASE_DIALOG中添加一个编辑框IDC_DBNAME和一个按钮IDC_BTN_CREATE,编辑框用以输入数据库名称。
使用ClassWizard给编辑框创建一个CString变量m_dbName。
双击IDC_BTN_CREATE按钮,并编辑OnBtnCreate()函数如下:
void CADOXCreateDatabaseDlg::OnBtnCreate()
{
//使输入到编辑框IDC_DBNAME的内容更新到m_dbName变量中
UpdateData(TRUE);
CString str;
str="d:\\"+m_dbName+".mdb";
//检查该数据库是否已经存在,如果该数据库已经存在,弹出消息框,返回
//使用API函数PathFileExists()检查路径文件是否存在
//请注意:为了使用API函数PathFileExists(),需要加入
//#include "Shlwapi.h"
//#pragma comment(lib,"shlwapi.lib")
if(PathFileExists(str))
{
CString strTemp;
strTemp.Format("%s已存在!",str);
AfxMessageBox(strTemp);
return ;
}
//定义ADOX对象指针并初始化为NULL
//用ADOX创建access数据库方法很简单,
//只需要新建一个Catalog对象,然后调用它的Create方法就可以了。
//Catalog是 ADOX 的一个对象,它包含描述数据源模式目录的集合。
//在这里,您只需知道创建数据库时使用这个对象就可以了。
//注意用try...catch组合捕捉错误
_CatalogPtr m_pCatalog = NULL;
CString DBName="Provider=Microsoft.JET.OLEDB.4.0;Data source=";
DBName=DBName+str;
try
{
m_pCatalog.CreateInstance(__uuidof(Catalog));
m_pCatalog->Create(_bstr_t((LPCTSTR)DBName));
}
catch(_com_error &e)
{
AfxMessageBox(e.ErrorMessage());
return ;
}
}
使用ADOX,需要引入ADOX的动态链接库msadox.dll,即在stdafx.h中加入如下语句:
#import "C:\Program Files\Common Files\system\ado\msadox.dll" no_namespace rename("EOF","adoEOF")
另外,ADOX属于COM对象,所以要在CADOXCreateDatabaseApp::InitInstance()函数中加入:
if(!AfxOleInit())
{
AfxMessageBox("OLE初始化出错!");
return FALSE;
}
初始化COM。
编译并运行该例程,对于编译过程中弹出的4146号警告不要理会。在编辑框中输入一个数据库名称,点击“创建数据库”按钮,该数据库将在d盘根目录下创建,再次输入该数据库名称并点击“创建数据库”按钮,将弹出警告对话框。
在VC中使用ADO的时候会得到4146号警告信息,我们可以不去理会,也可以通过#pragma warning指令解决,方法为在stdafx.h中加入的语句:
#import "C:\Program Files\Common Files\system\ado\msadox.dll" no_namespace rename("EOF","adoEOF")
前后再加一条语句,修改后为:
#pragma warning (disable:4146)
#import "C:\Program Files\Common Files\system\ado\msadox.dll" no_namespace rename("EOF","adoEOF")
#pragma warning (default:4146)
指令#pragma warning (disable:4146) 暂时屏蔽编译时4146警告信息
指令#pragma warning (default:4146) 重置编译器的4146警告到默认状态
ADO创建ACCESS数据库的表
我们一般用ADOX创建数据库,然后再用ADO创建数据库的表。
例程CREATE_DB_AND_TABLE演示如何使用ADO创建ACCESS数据库的表。
打开VC++ 6.0,新建一个基于对话框的工程CREATE_DB_AND_TABLE。在对话框IDD_CREATE_DB_AND_TABLE_DIALOG中添加如下控件:
控件名称 | ID | 用途 |
编辑框 | IDC_DBNAME | 输入数据库名称 |
按钮 | IDC_BTN_CREATE | 创建数据库 |
编辑框 | IDC_TABLENAME | 输入表名 |
按钮 | IDC_BTN_CREATE_TABLE | 创建表 |
使用ClassWizard给两个编辑框创建CString变量:
编辑框 | CString变量 |
编辑框IDC_DBNAME | m_dbName |
编辑框IDC_TABLENAME | m_tableName |
双击IDC_BTN_CREATE按钮,并编辑OnBtnCreate()函数如下:
void CADOXCreateDatabaseDlg::OnBtnCreate()
{
UpdateData(TRUE);
CString str;
str="d:\\"+m_dbName+".mdb";
if(PathFileExists(str))
{
CString strTemp;
strTemp.Format("%s已存在!",str);
AfxMessageBox(strTemp);
return ;
}
_CatalogPtr m_pCatalog = NULL;
CString DBName="Provider=Microsoft.JET.OLEDB.4.0;Data source=";
DBName=DBName+str;
try
{
m_pCatalog.CreateInstance(__uuidof(Catalog));
m_pCatalog->Create(_bstr_t((LPCTSTR)DBName));
}
catch(_com_error &e)
{
AfxMessageBox(e.ErrorMessage());
return ;
}
}
以上代码例程ADOXCreateDatabase中已经详细叙述。
双击IDC_BTN_CREATE_TABLE按钮,并编辑OnBtnCreateTable()函数如下:
void CCREATE_DB_AND_TABLEDlg::OnBtnCreateTable()
{
//先判断表名编辑框是否为空
UpdateData(TRUE);
if(!m_tableName.IsEmpty())
{
ADOX::_CatalogPtr m_pCatalog=NULL;
ADOX::_TablePtr m_pTable=NULL;
CString str;
str="d:\\"+m_dbName+".mdb";
CString DBName="Provider=Microsoft.JET.OLEDB.4.0;Data source=";
DBName=DBName+str;
//这段代码先检查表是否已经存在,如果表已经存在,不再创建,直接返回。
//其实这段代码不必深入研究,只需知道它的功能,直接拿来使用即可
try
{
m_pCatalog.CreateInstance(__uuidof(ADOX::Catalog));
m_pCatalog->PutActiveConnection(_bstr_t(DBName));
int tableCount=m_pCatalog->Tables->Count;
int i=0;
while(i<tableCount)
{
m_pTable=(ADOX::_TablePtr)m_pCatalog->Tables->GetItem((long)i);
CString tableName=(BSTR)m_pTable->Name;
if(tableName==m_tableName)
{
AfxMessageBox("该表已经存在!");
return;
}
i++;
}
}
catch(_com_error &e)
{
AfxMessageBox(e.Description());
return;
}
ADODB::_ConnectionPtr m_pConnection;
//创建表
_variant_t RecordsAffected;
try
{
m_pConnection.CreateInstance(__uuidof(ADODB::Connection));
//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 阻止其它程序或对象以任何权限建立连接
m_pConnection->Open(_bstr_t(DBName),"","",ADODB::adModeUnknown);
}
catch(_com_error e)
{
CString errormessage;
errormessage.Format("连接数据库失败!\r错误信息:%s",e.ErrorMessage());
AfxMessageBox(errormessage);
return;
}
try
{
CString strCommand;
/* 执行SQL命令:CREATE TABLE创建表格 该表包含三个字段:记录编号 INTEGER,姓名 TEXT,出生年月 DATETIME SQL语言中的create table语句被用来建立新的数据库表格。
create table语句的使用格式如下:
create tablename (column1 data type,column2 data type,column3 data type);
如果用户希望在建立新表格时规定列的限制条件,可以使用可选的条件选项
create table tablename
(column1 data type[constraint],
column2 data type[constraint],
column3 data type[constraint]);
举例:
create table employee
(firstname varchar(15),
lastname varchar(20),
age number(3),
address varchar(30),
city varchar(20));
简单来说,创建新表格时,在关键词create table后面加入所要建立的表格的名称,然后在括号内顺次设定各列的名称,数据类型,以及可选的限定条件等。
使用SQL语句创建的数据库表格和表格中列的名称必须以字母开头,后面可以使用字母,数字或下划线,名称的长度不能超过30个字符,注意,用户在选择表格名称时不要使用SQL语言中的保留关键字,如select,create,insert等,作为表格或列的名称。
*/
strCommand.Format("CREATE TABLE %s(记录编号 INTEGER,姓名 TEXT,出生年月 DATETIME)",m_tableName);
//Execute(_bstr_t CommandText,VARIANT* RecordsAffected,long Options)
//其中CommandText是命令字串,通常是SQL命令,
//参数RecordsAffected是操作完成后所影响的行数
//参数Options表示CommandText中内容的类型,可以取下列值之一:
//adCmdText 表明CommandText是文本命令
//adCmdTable 表明CommandText是一个表名
//adCmdProc 表明CommandText是一个存储过程
//adCmdUnknown 未知
m_pConnection->Execute(_bstr_t(strCommand),&RecordsAffected,ADODB::adCmdText);
if(m_pConnection->State)
m_pConnection->Close();
}
catch(_com_error &e)
{
AfxMessageBox(e.Description());
}
}
}
这段代码先用ADOX的Catalog对象检查表是否已经存在,如果该表已经存在,直接返回;如果还没有该表,使用ADO的Connection对象的Execute函数创建表。
Connection对象的用法:首先定义一个Connection类型的指针,然后调用CreateInstance()来创建一个连接对象的实例,再调用Open函数建立与数据源的连接。最后使用Execute()函数执行SQL语句创建表。
关于调用CreateInstance()来创建连接对象的实例,还需作一点说明。ADO库包含三个基本接口:_ConnectionPtr接口,_RecordsetPtr接口和_CommandPtr接口。其分别对应Connection对象(完成应用程序对数据源的访问连接),Recordset对象(将查询的结果以记录集的方式存储)和Command对象(对已连接的数据源进行命令操作)。
_ConnectionPtr m_pConnection;
_RecordsetPtr m_pRecordset;
_CommandPtr m_pCommand;
而这三个对象实例的创建,可以使用如下语句:
m_pConnection.CreateInstance(__uuidof(Connection));
或者:
m_pConnection.CreateInstance(“ADODB.Connection”);
m_pRecordset.CreateInstance(__uuidof(Recordset));
或者:
m_pRecordset.CreateInstance(“ADODB.Recordset”);
m_pCommand.CreateInstance(__uuidof(Command));
或者:
m_pCommand.CreateInstance(“ADODB.Command”);
两种方法的作用完全相同,使用哪种方法,完全是您的个人爱好问题。
如例程ADOXCreateDatabase,在BOOL CCREATE_DB_AND_TABLEApp::InitInstance()函数中加入:
if(!AfxOleInit())
{
AfxMessageBox("OLE初始化出错!");
return FALSE;
}
在stdafx.h中加入如下语句:
#import "C:\Program Files\Common Files\system\ado\msadox.dll"
#import "C:\Program Files\Common Files\system\ado\msado15.dll" rename("EOF","adoEOF")
关于这两条语句,需要进行特别说明:
由于该例程同时使用ADOX和ADO,需要同时引入msado15.dll和msadox.dll两个库。这两个库的名字空间是不同的,msado15.dll的名字空间是ADODB,msadox.dll的名字空间是ADOX。在使用ADO所属的名字空间的变量,函数时,在前面加上ADODB::,在使用ADOX所属的名字空间的变量,函数时,在前面加上ADOX::。
另外,一般ADOX和ADO分开操作。您也可以在ADOX操作部分使用using namespace ADOX::,而在ADO操作部分使用using namespace ADO:,以区分名字空间。这样,您就不必再使用ADOX::和ADODB::了。
rename(“EOF”,”adoEOF”) //重命名EOF是必要的,因为典型的VC应用都已经定义了EOF作为常数-1,为了避免冲突,将ADO中的EOF重命名为adoEOF。
#import中有一个属性为no_namespace,这是告诉编译器该类不在一个单独的名字空间中,使用no_namespace意味着你不需要在初始化变量的时候引用名字空间。当然如果在您的应用中需要导入多个类型库的话,不要使用no_namespace,以免引起名字冲突。
再通俗一点讲,就是只导入一个类型库的话,可以在#import语句中加入no_namespace属性,您的程序可以直接使用这个类型库的名字空间的内容,而不必使用using namespace XXX;或XXX::,这是因为no_namespace属性告诉编译器该类型库不再名字空间,而是在全局空间上工作;如果您导入几个类型库,而这几个类型库之间没有定义冲突,您也可以在使用no_namespace属性;但如果两个类型库中有定义冲突,就不能使用no_namespace属性,如果使用no_namespace属性,就会在全局空间产生定义冲突。
对于本例程,您可以把stdafx.h中的
#import "C:\Program Files\Common Files\system\ado\msadox.dll"
#import "C:\Program Files\Common Files\system\ado\msado15.dll" rename("EOF","adoEOF")
改为
#import "C:\Program Files\Common Files\system\ado\msadox.dll"
#import "C:\Program Files\Common Files\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF")
这样改动后,void CCREATE_DB_AND_TABLEDlg::OnBtnCreateTable()中的ADODB::需要完全省略掉。
当然,您也可以把这两行改为:
#import "C:\Program Files\Common Files\system\ado\msadox.dll" no_namespace
#import "C:\Program Files\Common Files\system\ado\msado15.dll" rename("EOF","adoEOF")
但这样改动后,void CCREATE_DB_AND_TABLEDlg::OnBtnCreateTable()中的ADOX::需要完全省略掉。
由于ADOX和ADO有定义冲突,也就是说,msado15.dll和msadox.dll有相同的定义部分,所以在一个程序中,不允许同时使用no_namespace。
使用_ConnectionPtr接口开发ACCESS数据库
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数据库。
使用_RecordsetPtr接口开发ACCESS数据库
_RecordsetPtr智能指针,它是专门为通过记录集操作数据库而设立的指针,通过该接口可以对数据库的表内的记录、字段等进行各种操作。
要搞清楚:数据库和ADO的记录集是两个不同的概念, 是存在于不同物理位置的两个存储空间。 记录集相当于是实际数据的一份拷贝。 正因为记录集是相对脱离数据库而存在的, 所以才存在后面将要介绍的Open方法中涉及的光标类型和锁定类型这两个问题。
_RecordsetPtr接口的使用方法:
先创建记录集对象:
_ConnectionPtr m_pRecordset;
m_pRecordset.CreateInstance(__uuidof(Recorset));
创建记录集对象后,只是为它分配内存空间,记录集中不含任何数据。记录集对象是用来获得数据库中的数据并对其操作的,所以还要打开记录集,从数据库中取得数据记录。可有多种方法打开记录集,下面只介绍最常用的Open方法:
为记录集对象分配了空间后就可以用Open函数打开记录集,该函数原型为:
HRESULT Recordset15::Open(const _variant_t& Source,const _variant_t& ActiveConnection,enum CursorTypeEnum CursorType,enum LockTypeEnum LockType,long Options)
参数:
Source是数据查询字符串。
ActiveConnection是已经建立好的连接(我们需要用Connection对象指针来构造一个_variant_t对象)。
CursorType 光标类型,它是枚举CursorTypeEnum中的一个值。
LockType 锁定类型 它是枚举LockTypeEnum中的一个值
Options 指定Source的类型
光标类型CursorType,可取如下值之一:
- adOpenUnspecified=-1 不作特别指定
- adOpenForwardOnly=0 默认值,前滚静态光标。这种光标只能向前浏览记录集,比如用MoveNext向前滚动,这种方式可节省资源,提高浏览速度,但诸如BookMark、RecordCount、AbsolutePosition、AbsolutePage都不能使用。
- adOpenKeyset=1 键集游标,采用这种光标的记录集看不到其它用户的新增、删除操作,但对于更新原有记录的操作对你是可见的。
- adOpenDynamic=2 动态光标,所有数据库的操作都会立即在用户记录集上反应出来。
- adOpenStatic=3 静态游标。它为记录产生一个静态备份,其他用户的新增、删除、更新操作对你的记录集来说是不可见的。
LockType锁定类型,它可以是以下值之一,请看如下枚举结构
enum LockTypeEnum
{
adLockUnspecified=-1, //未指定
adLockReadOnly=1, //只读记录集,默认值。无法更改数据。
adLockPessimistic=2, //悲观锁定方式。只有在调用Update方法时才锁定记录。这是最安全的锁定机制
adLockOptimistc=3, //乐观锁定方式,只有在你调用Update方法时才锁定记录。
在此之前仍然可以做数据的更新、插入、删除等操作
adLockBatchOptimistic=4 //乐观分批更新。编辑时记录不会锁定,更改、插入及删除是在批处理模式下完成。
}
关于光标和锁定类型,对于一般用户,建议您只作简单了解,后面将进一步进行解说。
Options可以取如下值之一:
adCmdText: 表明CommandText是文本命令。
adCmdTable:表明CommandText是一个表名。
adCmdProc:表明CommandText是一个存储过程。
adCmdUnknown:未知。
例如:假设m_pConnection是我们已经建立好的连接,我们使用_RecordsetPtr接口的Open方法打开Employees表的记录集的语句如下:
m_pRecordset->Open(“SELECT * FROM Employees”,
_variant_t((IDispatch*)m_pConnection,true),
adOpenStatic,
adLockOptimistic,
adCmdText);
例程RecordsetPtr演示使用_RecordsetPtr指针通过记录集操作数据库。
打开VC++ 6.0,新建一个基于对话框的工程RecordsetPtr。在对话框IDD_RECORDSETPTR_DIALOG中进行编辑:
使用三个Group Box分成四个部分,第一部分演示如何读取数据库数据;第二部分演示如何修改数据库;第三部分演示如何向数据库中插入数据;第四部分演示如何删除数据库中的数据。
使用ClassWizard给列表框IDC_LIST1创建CListBox变量m_list1:
双击IDC_BTN_READREC按钮,并编辑OnBtnReadrec()函数如下:
void CRecordsetPtrDlg::OnBtnReadrec()
{
_ConnectionPtr m_pConnection;
_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");
m_pRecordset->Open("SELECT EmployeeID,FirstName,LastName,HireDate,City FROM Employees WHERE City='London'",
_variant_t((IDispatch*)m_pConnection,true),
adOpenStatic,
adLockOptimistic,
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;
}
该段代码演示了如何读取数据库表内的数据。其原理是如果没有遇到表结束标志adoEOF,则用GetCollect(字段名)来获取当前记录指针所指的字段值,然后再用MoveNext()方法移动到下一条记录位置。
双击IDC_BTN_CHANGE按钮,并编辑OnBtnChange()函数如下:
void CRecordsetPtrDlg::OnBtnChange()
{
_ConnectionPtr m_pConnection;
_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");
m_pRecordset->Open("SELECT EmployeeID,FirstName,LastName,HireDate,City FROM Employees WHERE (City='London') AND (EmployeeID=6)",
_variant_t((IDispatch*)m_pConnection,true),
adOpenStatic,
adLockOptimistic,
adCmdText);
}
catch(_com_error &e)
{
AfxMessageBox(e.Description());
}
try
{
while(!m_pRecordset->adoEOF)
{
m_pRecordset->PutCollect("LastName",_variant_t("Jackson"));
m_pRecordset->MoveNext();
}
m_pRecordset->Update();
}
catch(_com_error* e)
{
AfxMessageBox(e->ErrorMessage());
}
m_pRecordset->Close();
m_pRecordset=NULL;
m_pConnection->Close();
m_pConnection=NULL;
}
该段代码演示了如何修改记录中的字段值:
将记录指针移动到要修改记录的位置处,直接用PutCollect(字段名,值)将新值写入并Update()更新到数据库即可。
移动记录指针可以通过MoveFirst()方法移动到第一条记录,MoveLast()方法移动到最后一条记录,MovePrevious()方法移动到当前记录的前一条记录,MoveNext()方法移动到当前记录的下一条记录。也可以使用Move(记录号)移动记录指针到需要位置。注意:Move()方法是相对于当前记录来移动指针位置的。正值向后移动,负值向前移动。如Move(3),当前记录是3时,它将从记录3开始往后再移动3条记录位置。关于移动记录指针后面将会用到。
双击IDC_BTN_NEW按钮,并编辑OnBtnNew()函数如下:
void CRecordsetPtrDlg::OnBtnNew()
{
_ConnectionPtr m_pConnection;
_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");
m_pRecordset->Open("SELECT * FROM Employees",
_variant_t((IDispatch*)m_pConnection,true),
adOpenStatic,
adLockOptimistic,
adCmdText);
}
catch(_com_error &e)
{
AfxMessageBox(e.Description());
}
try
{
m_pRecordset->MoveLast();
m_pRecordset->AddNew();
m_pRecordset->PutCollect("EmployeeID",_variant_t((long)10));
m_pRecordset->PutCollect("FirstName",_variant_t("Mary"));
m_pRecordset->PutCollect("LastName",_variant_t("Williams"));
m_pRecordset->PutCollect("HireDate",_variant_t("15/4/1993 12:00:00"));
m_pRecordset->PutCollect("City",_variant_t("New York"));
m_pRecordset->PutCollect("Country",_variant_t("USA"));
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}
m_pRecordset->Update();
m_pRecordset->Close();
m_pRecordset=NULL;
m_pConnection->Close();
m_pConnection=NULL;
}
该段代码演示如何插入记录:
先用AddNew()方法新增一个空记录,再用PutCollect(字段名,值)输入每个字段的值,最后用Update()更新到数据库即可。
双击IDC_BTN_DELETE按钮,并编辑OnBtnDelete()函数如下:
void CRecordsetPtrDlg::OnBtnDelete()
{
_ConnectionPtr m_pConnection;
_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");
m_pRecordset->Open("SELECT * FROM Employees",
_variant_t((IDispatch*)m_pConnection,true),
adOpenStatic,
adLockOptimistic,
adCmdText);
}
catch(_com_error &e)
{
AfxMessageBox(e.Description());
}
try
{
//假设删除第10条记录
m_pRecordset->MoveFirst();
m_pRecordset->Move(9);
m_pRecordset->Delete(adAffectCurrent);
//参数adAffectCurrent为删除当前记录
m_pRecordset->Update();
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}
m_pRecordset->Close();
m_pRecordset=NULL;
m_pConnection->Close();
m_pConnection=NULL;
}
该段代码演示了如何删除记录:
先将记录指针移动到要删除的记录的位置,直接用Delete()方法删除它,并用Update()来更新数据库即可。
使用记录集操作完毕后要关闭记录集:
直接用Close()方法关闭记录集并赋予其空值。代码如下:
m_pRecordset->Close();
m_pConnection->Close();
m_pRecordset=NULL;
m_pConnection=NULL;
使用_RecordsetPtr接口开发ACCESS数据库就介绍到这里,它可以更加灵活地操作数据库。当然,您还可以使用_CommandPtr接口开发ACCESS数据库,它提供了一个简单的方法来执行返回记录集的SQL语句。本文不讲解_CommandPtr接口。
用心的读者您可能已经发现,使用Connection对象的Execute方法可以完成数据库的操作,使用Recordset也可以完成同样功能的数据库操作。我们应该采用哪种方法呢?另外,Connection对象的Execute方法返回一个记录集,Recordset的Open方法也打开一个记录集,二者有什么区别呢?下面我们将进行说明。
前面已经讲过,数据库和ADO的记录集是两个不同的概念,是存在于不同物理位置的两个存储空间。记录集相当于是实际数据的一份拷贝。正因为记录集是相对脱离数据库而存在的,所以才光标类型和锁定类型这两个问题。
在记录集被创建以后,数据提供者负责在数据库和记录集之间进行侦测,记录集的类型決定了提供者在多大程度上确保数据库与记录集之间的一致性,这通常是由光标类型決定的,而同时也決定了提供者采取什么方式来确保用戶在更新数据库时,本次更新的完整性,这是由锁类型決定的。
比方说,一个用户正在对一个记录集做一次更新,并试图将此更新应用到数据库中。因为通过记录集来更新数据是分两步完成的,第一步是修改记录集的內容,第二步才是将修改通过数据提供者更新到数据库中,两步之间存在时间上的细微差別,所以,他可能会遇到一个问题,即他的第一步到第二步的操作,是否会因为这期间同时遇到其他用户的操作而失败。
对数据库的部分记录进行锁定解決了这个问题,而不同类型的锁,解決问题的方式也不一样。有的在第一步开始时就锁定数据库,有的在第二步开始时才锁定数据库。锁定占用了数据库资源,而使得记录集在对于使用者的更新操作方面更加可靠。
综上所述,可以得出一个结论:锁是由于记录集的存在而存在的,沒有记录集,或者說沒有可供更新的记录集,锁就沒有存在的意义。
从效率上考虑,锁会降低并发性,尤其是当同时连接数据库的用户增多时,不采用记录集来更新数据库总是更好的选择,虽然使用起来会更麻烦一些。
选择与数据库联系得较少的记录集总是能提高效率,因为提供者无需做很多的侦测工作。比如,动态的记录集总是实时地将数据库的状态反映到记录集中,这很有用,但也会耗费无提供者更多的精力。如果无需更新数据,只读的记录集是与数据库联系得最少的一种,选择使用这种记录集来代替其它类型在效率上通常会有很明显的提高。
Connection对象的Execute方法只能得到一个只能前移的、只读的记录集。
使用RecordAffected参数找出有多少条记录受到影响。一旦Execute命令执行完毕,受SQL命令影响的记录数就返回到RecordAffected参数中。
Connection的Execute方法不支持任何锁定类型,这就是和Recordset的Open方法的主要区别所在,所以用Recordset的Open方法将更灵活,而Connection的Execute方法更简洁高效。
Execute是Connection对象对数据库操作的一个方法,你可以把它理解为执行SQL语句,也就是说Execute是一个操作方法,而不是像open方法返回一个记录集,然后再对记录集操作。
尽量使用SQL语句进行数据库操作。尽管采用Recordset对象来更新数据是非常方便的,但是它的开销也更大,所以如果可能的话,就要采用SQL语句来更新数据。
Execute返回的记录集是只读的,不可以更新,所以也就不存在锁。这个记录集不具备Recordset可以改变光标类型和锁定类型的功能,它只能返回一个只能前移的、只读的记录集。
尽管很多开发人员都习惯采用“SELECT * FROM 表名”的模式进行查询,但是为了提高系统的效率,如果你只需要其中某几个字段值的话,最好把这几个字段直接写出来,同时需要限定返回记录集的范围(通过WHERE子句进行限定)。