分享
 
 
 

用VC++6.0开发多表联接的数据库应用程序

王朝c/c++·作者佚名  2006-01-06
窄屏简体版  字體: |||超大  

用VC++6.0开发多表联接的数据库应用程序

摘要

本文介绍了用Visual C++6.0开发数据库应用程序时,使用MFC ODBC类的编程方法,详细说明了在MFC ODBC的派生类中设置SQL语句参数的方法,实现了二个表的关联。

关键字

数据库 ,多表联接,MFC ODBC

1.引言

开发Windows应用程序时,在很多情况下可能要和数据库连接。数据库类型多种多样,功能结构也各不相同。从比较简单的DBASE、FoxPro等到复杂的SYBASE、Qracle等大型数据库系统。VC++6.0都提供了一些接口。程序员可利用这些接口方便地开发数据库应用程序。MFC ODBC类就是其中的一个,在快速生成简单一致的接口应用程序方面这些类非常有用。用户不必了解ODBC API和SQL的具体细节,利用ODBC类即可完成对数据库的大部分操作。然而,VC++ Appwizard 生成的数据库应用程序,只是基于单个数据表的数据库应用程序。而实际应用中,往往要求数据库应用程序能关联二个或多个数据表。VC++的好多书籍对此只是简单的介绍。本文透彻地解决这一问题。

2.ODBC与MFC

2.1 ODBC

ODBC(开放数据库互连)应用程序可通过ODBCAPI访问不同数据源中的数据,每个不同的数据源类型由一个ODBC驱动程序支持,这个驱动程序完成了ODBC API程序的核心,并与具体的数据库通信。ODBC环境提供了驱动程序管理器(Driver Manager),管理那些与不同数据源连接的驱动程序在ODBC32.DLL中执行。应用程序只需要与驱动程序管理器连接,驱动程序管理器就会根据应用程序提供的数据源名,选择正确的驱动程序来访问数据源。

要使用ODBC来开发数据库应用程序,必须使用在控制面板处的ODBC数据源管理器,来建立、配制数据源。应本例应用程序需要,按以下步骤建立所需的数据源。

1. 双击控制面板处的32位的ODBC程序,选择对话框中的User DSN(用户数据源名)选项卡。

2. 单击Add按钮,然后选择一个数据源:Microsoft Visual FoxPro Driver。单击“完成”,进入下一步设置。

3. 在Data Source Name域内输入数据源名:DB-FSB。然后选择Visual FoxPro数据库的位置。

4. 单击OK按钮,返回到控制面板。

2.2 MFC ODBC

MFC的数据库扩展部分封装了使用ODBC数据资源的细节,提供了VC++与ODBC间一种简单的调用接口。MFC的ODBC类主要包括:用来与一个数据源相连的CDatabase类;用来处理从数据库返回的一组记录集的CRecordset类;简化从Crecordset对象中得到数据的显示的CRecordView类。

虽然Cdatabase类允许你对一个数据库执行SQL语句,但是CRecordset类提供了应用程序与数据交互的实质。本例应用程序使用CRecordset类来操作数据源.

CRecordset类的主要目的是让应用程序访问从数据库中返回的结果集。在应用程序中要使用CRecordset类,可根据数据源并使用VC++中的ClassWizard来创建Crecordset派生类。通常,一个CRecordset派生类对应用户数据源中的一个表。每生成一个Crecordset派生类,就要选择一个数据源和一个数据源中的一个表。若生成一个Crecordset派生类时,选择了一个数据源中的多个表,那么Crecordset派生类中的结果集是多个表的卡氏积(迪卡尔积)连接,显然,在实际应用中没什么意义。应用程序通过派生出的Crecordset类可对记录集中的记录进行滚动、修改、增加和删除等操作。

CRecordView类具有几个增强功能,允许使用对话框方式(DoDataExchange()函数)直接从记录集显示数据,使得从记录集中显示数据更为容易。并提供了记录移动等操作。

3. 多表联接的数据库应用程序

3. 1本例程序功能:

通过FSB表的BZM字段及DBK1表的HH字段,将Visual FoxPro 的 FSB表与DBK1表(结构如下)关联起来。程序运行出现界面如图1。用鼠标点击工具条的?、?、(、(则FSB表记录移动而DBK1表的记录没移动。用鼠标点击“关联”按钮,则DBK1表的记录和FSB表记录同步移动(BZM编辑框内容与HH编辑框内容相同)。如果,要求按照具体的关键字值来查询二个表中的相关记录,那么,在“定位” 编辑框中输入具体的关键字值,然后,用鼠标点击“关联”按钮,就会见到二个表在新的记录集实现关联。

表1: FSB表结构

字段名

类型

备注

BZM

C

索引关键字

DGDL1

N

DGZD1

N

其它字段

表2: DBK1表

字段名

类型

备注

HH

C

索引关键字

BL

N

ZZCM

C

其它字段

基于MFC ODBC类开发的数据库应用程序,是通过MFC ODBC类使用SQL语句方式操纵数据表的。数据库中表FSB与表DBK1关联查询的SQL语句是:

SELECT * FROM FSB,DBK1 WHERE FSB.BZM=DBK1.HH

由于创建一个CRecordset派生类时,一般只选择一个数据源中的一个表,因此基于MFC ODBC类开发的数据库应用程序要实现二个表关联,就要使用CRecordset类的参数m _strFilter。它相当于SQL语句中的WHERE子句。参数m _strSort相当于SQL语句中的GROUP BY子句。要注意m_strFilter字符串中不要包含“WHERE”关键字。本例在表FSB与表DBK1对应的CRecordset派生类中分别使用了mbzm和mhh二个m _strFilter参数。用鼠标点击“关联”按钮时,程序首先根据“定位” 编辑框中的内容作为mbzm的值,在表FSB检索结果集。表DBK1对应的CRecordset派生类根据表FSB对应的CRecordset派生类的当前记录m_bzm值,作为mhh的值实行检索,从而得到与表FSB关键字段BZM对应的表DBK1的记录。实现了表FSB与表DBK1的关联。由此可见,二表关联的关键是m _strFilter参数的设置。

图 1

3.2 数据库应用程序创建

3.2. 1 创建单表单文档的数据库应用程序

根据前面建立的数据源DB-FSB,使用VC++ Appwizard 生成一个单表单、单文档的数据库应用程序。选择数据源DB-FSB的数据表时应选择FSB.DBF。应用程序名为ZF0001(具体步骤可参考有关VC++资料)。ZF0001应用程序中创建了CZf0001Doc、CZf0001Set、CZf0001View等派生类。

3.2.2 设置m _strFilter参数

在上一步生成的CZf0001Set类中,按以下方式,在① ② ③程序中设置m _strFilter参数(黑体部分的语句都是为CZf0001Set的参数mbzm而手动增加的)。为节省篇幅,省略程序清单的部分内容。

①. 在Crecordset派生类的定义中,描述了被连接的数据源表的字段,并在VC++ Appwizard 生成的程序注释“// Field/Param Data ”中提示在此可定义参数。

CRecordset派生类:CZf0001Set的定义

class CZf0001Set : public Crecordset //Crecordset派生类CZf0001Set

{

public:

CZf0001Set(CDatabase* pDatabase = NULL);

DECLARE_DYNAMIC(CZf0001Set)

// Field/Param Data

//{{AFX_FIELD(CZf0001Set, CRecordset) //被绑定的字段

CString m_bzm;

CString m_dgqd1;

、、、、、、 //为节省篇幅,省略部分字段

CString m_bz;

//}}AFX_FIELD

CString mbzm; // 参数mbzm

// Overrides

// ClassWizard generated virtual function overrides

、、、、、、、

virtual void Dump(CDumpContext& dc) const;

#endif

};

②.Crecordset派生类:CZf0001Set的构造函数

其中,对被绑定字段的相应内存变量进行了初始化。

CZf0001Set::CZf0001Set(CDatabase* pdb) : CRecordset(pdb)

{

//{{AFX_FIELD_INIT(CZf0001Set)

m_bzm = _T("");

m_dgqd1 = _T("");

、、、、、、

m_bz = _T("");

m_nFields = 16; //数据源表的记录字段个数

//}}AFX_FIELD_INIT

m_nDefaultType = snapshot;

m_nParams=1; // CZf0001Set的参数个数

mbzm=""; //参数初始化

}

③.记录字段交换(RFX)

通过使用RFX,MFC框架可以在数据库和CRecordset类变量之间交换。交换是通过执行DoFieldExchange()函数而建立的。

void CZf0001Set::DoFieldExchange(CFieldExchange* pFX)

{

//{{AFX_FIELD_MAP(CZf0001Set)

pFX->SetFieldType(CFieldExchange::outputColumn);

RFX_Text(pFX, _T("[bzm]"), m_bzm);

RFX_Text(pFX, _T("[dgqd1]"), m_dgqd1);

、、、、、、;

RFX_Text(pFX, _T("[dgdl2]"), m_dgdl2);

RFX_Text(pFX, _T("[bz]"), m_bz);

//}}AFX_FIELD_MAP

pFX->SetFieldType(CFieldExchange::param);

//把字段类型设为CFieldExchange::param

RFX_Text(pFX,"mbzm",mbzm); //为参数设置RFX 宏,如果有多个参数,必须按SQL的语句中的位置标志符的顺序设置,RFX 宏中的参数的名字如"mbzm",并非用来与参数匹配,可以自己定义。

}

3.2.3 增加第二个表,并设置第二个表的参数

在3。2。1创建的数据库应用程序基础上,进入ClassWizard,点击Add Class...按钮并在弹出的菜单中选择New...,然后在Create New Class对话框中的Name栏中输入CZf1001,在Base class栏中选择CRecordset,按Create按钮。

在弹出的Database Options对话框中,在ODBC组合框里选择DB-FSB数据源。然后按OK按钮。在弹出的Select Database Tables对话框中选择DBK1表。按OK确认。并在所有存在 #include "CZf0001Set.h" 的文件中,都加入#include "CZf1001.h" 。这样就创建了与DBK1表对应的Crecordset派生类。

在第一步创建的CZf0001Doc类中,增加一个CZf1001 对象的指针变量m_zf1002(即:CZf1001* m_zf1002)。

按3.2.2介绍的CZf0001Set类m _strFilter参数的设置方法,在CZf1001类中,设置参数mhh。

3. 3 参数mhh及参数mbzm在CrecordView的派生类CZf0001View中的使用

3.3.1 参数在CZf0001View::OnInitialUpdate()函数使用

在CZf0001View::OnInitialUpdate()函数的开头部分,调用CZf0001View:: GetDocument()从文档类CZf0001Doc类中,返回二个CrecordSet类(CZf0001Set、CZf1001)的指针。根据返回的指针,设置m _strFilter (相当于SQL语句的WHERE子句),并确定二个参数的初始值。这里要说明一点:

m_pSet->m_strFilter="BZM like ?";

m_pSet2->m_strFilter="hh like ?";

语句中的“?”,在调用Open或Requery时,“?"将分别自动地被CZf0001Set::mbzm和 CZf1001::mhh的值取代。例如,指定mbzm为“31001",则m_pSet->m_strFilter将变成"BZM =31001"。这样用户只要指定了mbzm,就可以得到所需要的记录集。CZf0001View::OnInitialUpdate()的程序清单如下(黑体部分的语句是手工增加的):

void CZf0001View::OnInitialUpdate()

{ m_pSet = &GetDocument()->m_zf0001Set;

m_pSet2=&GetDocument()->m_zf1002;

if(!m_pSet2->Open())

return;

m_pSet->m_strFilter="BZM like ?";

m_pSet->mbzm= "%"; //初始选择所有记录

m_pSet->m_strSort="";

m_pSet2->m_strFilter="hh like ?";

m_pSet2->mhh=m_pSet->m_bzm; //将表FSB对应的CRecordset派生类的m_bzm的值,作为参数mhh的值

m_pSet2->m_strSort=""; //检索的结果不进行排序

m_pSet->m_pDatabase= m_pSet2->m_pDatabase; //共享CDatabase

CRecordView::OnInitialUpdate();

GetParentFrame()->RecalcLayout();

ResizeParentToFit();

}

3.3.2 在对话框中加入编辑框

在资源视图Dialog的IDD_ZF0001_FORM表单中,加入用户需要的编辑框。用ClassWizard在第一个表FSB中选择有关字段与它们相连。但是.使用ClassWizard无法找到第二个表DBK1字段变量,因此,对于计划与第二个表DBK1字段相连的编辑框,必须用手工修改CRecordView类的DoDataExchange()(对话框数据交换函数)。 在DoDataExchange()函数 “//}}AFX_DATA_MAP” 后面加入有关内容。见下面程序的黑体部分。如果黑体部分语句加在“//}}AFX_DATA_MAP”的前面,那么,要再次修改IDD_ZF0001_FORM表单时,就无法使用ClassWizard.

void CZf0001View::DoDataExchange(CDataExchange* pDX)

{

CRecordView::DoDataExchange(pDX);

//{{AFX_DATA_MAP(CZf0001View)

DDX_Control(pDX, IDC_COMBO1, m_comb);

DDX_Control(pDX, IDC_EDIT4, m_SS);

DDX_FieldText(pDX, IDC_EDIT2, m_pSet->m_bl1, m_pSet);

DDX_FieldText(pDX, IDC_EDIT3, m_pSet->m_dgdl1, m_pSet);

DDX_FieldCBString(pDX, IDC_COMBO1, m_pSet->m_bzm, m_pSet);

DDX_FieldText(pDX, IDC_EDIT5, m_pSet->m_dgqd1, m_pSet);

//}}AFX_DATA_MAP

DDX_FieldText(pDX, IDC_EDIT1, m_pSet2->m_bl, m_pSet2);

DDX_FieldText(pDX, IDC_EDIT6, m_pSet2->m_hh, m_pSet2);

DDX_FieldText(pDX, IDC_EDIT7, m_pSet2->m_zzcm, m_pSet2);

}

3.3.3 在对话框中加入一个按钮

为演示二个表关联的效果,在对话框中加入一个“关联”按钮和一个输入参数用的"定位"编辑框。并给此按钮增加单击事件代码如下:

void CZf0001View::OnButton1()

{

// TODO: Add your control notification handler code here

char ll[11];

int nn=0;

m_SS.GetLine(0,ll); //读“定位”编辑框中的内容

nn=m_SS.LineLength(0); //读“定位”编辑框中的字符内容的长度

if(nn) //“定位”编辑框中有输入内容,则按内容检索形成新的记录集

{ m_pSet->mbzm=(CString)ll;

m_pSet->mbzm=m_pSet->mbzm.Left(nn)+"%"; //“%”由SQL语法规定代表任意长度(长度可以为0)的字符串

m_pSet->Requery(); //在表FSB中重新检索

if(m_pSet->IsEOF())

{ MessageBox("检索结果为空!");

UpdateData(FALSE);

m_pSet->mbzm="%";

m_pSet->Requery();

}

m_pSet2->Requery(); //以表1当前记录BZM值,在表2:DBK1中检索

UpdateData(FALSE); //刷新表单内容

m_SS.SetSel(0,-1);

m_SS.ReplaceSel(""); //清除“定位”编辑框中的内容,使下一次单击“关联”按钮时,不会重复检索(即使nn=0)

}

else //“定位”编辑框为空,则按表1现有的结果集与表2:DBK1关联

{

m_pSet2->mhh=m_pSet->m_bzm; //将表1当前记录BZM值,作为表DBK1的查找参数

m_pSet2->Requery();

UpdateData(FALSE);

m_pSet->MoveNext();

}

}

4. 小结

通过本例可知,虽然VC++提供了Appwizard和Classwizard, 为编程者节省了大量的工作,但是想编出优秀的VC++应用程序,仅凭这些是远远不够的;必须熟悉MFC类库、ActiveX控件等知识。本文作为一个例子介绍了二个表的联接方法。在实际的应用中,读者可以举一翻三,编制更加完善的数据库应用程序。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有