VS2019 C++ SQL Server 数据库连接
作者:mmseoamin日期:2023-12-27

总体来说VS有两种配置SQL Server的方式,一种是ADO,另一种是ODBC。这两种方式的查询我都有问题,查询到了结果数据不是乱码就是显示在list control上时值变了,我也不知道为什么。

ADO

1 什么是ADO 

以下ADO概念引用自 ADO数据库访问技术_lishan9133的博客-CSDN博客

ADO(active data object,活动数据对象),是一种基于COM(组件对象模型)的自动化接口技术,并以OLE DB(对象连接和镶入的数据库)为基础,经过OLE DB精心包装后的数据库访问技术,利用它可以快速的创建数据库应用程序。ADO提供了一组非常简单,将一般通用的数据访问细节进行封装的对象。由于ODBC数据源也提供了一般的OLE DB Privider,所以ADO不仅可以应用自身的OLE DB Privider,而且还可以应用所有的ODBC驱动程序。 

2 步骤

               1 引入ADO库文件,初始化COM组件

               2 声明Connection实例对象,并连接(打开)数据库

               3 利用连接对象 进行数据库操作

               4 结束操作,关闭连接,释放连接对象  

3 引入ADO库文件

#import "C:/Program Files/Common Files/System/ado/msado15.dll"  no_namespace rename("EOF","adoEOF")

这个路径下的msado.dll文件是已经存在的,不需要安装或者下载其他软件或库文件。有三点需要注意,也相当于四个问题吧。

第一:需要将外部文件引入到头文件中,在某个版本之后VS版本中pch.h就代替了那个s开头的头文件,这个无关紧要。

第二:引入这个文件路径的时候,VS编译器可能会报错,声称找不到这个文件,通过网上查找发现这个问题并不需要解决。也就是说出现这个BUG可能是编译器自己反应慢,这个错误在编译的时候也可以通过。

第三: no_namespace rename("EOF","adoEOF") 这条语句的作用是防止与其他库名中的EOF冲突。这里就不得不讲一讲EOF 和BOF了。BOF 指示当前记录位置位于记录集对象的第一个记录之前;EOF 指示当前记录位置位于记录集对象的最后一个记录之后。他们都是布尔值,即:只能为 True 或者 False。他们被用于判断记录指针是否越界,如果越界操作,程序运行会产生错误。如果没有打开记录集对象,BOF 和 EOF 都被设置为 True,RecordCount 属性值为 0;如果打开的记录集对象中至少包含一条记录,则第一条记录为当前记录,BOF 和 EOF 属性都为 False。

第四: 有些情况下会出现4996的错误,网上查找会有以下代码解决

项目属性->C/C++->SDL检查->关闭 ,然后在头文件定义之后再加上以下语句

#pragma warning(disable:4996)

4 初始化COM组件

HRESULT hr=CoInitialize(NULL);//ADO是COM组件,所以使用ADO连接数据库需要初始化COM组件
ASSERT(SUCCEEDED(hr)); //SUCCEEDED(hr)先判断COM是否初始化成功,如果不成功ASSERT的参数即为假,则会报告错误并终止程序

5 声明实例对象指针

    _ConnectionPtr pMyConnect; //连接对象指针
	_RecordsetPtr pRecord;  //记录集对象指针
	_CommandPtr pCommand; //命令对象指针

Connection对象:它表示到数据库的连接,管理应用程序和数据库之间的通信。Command和Recordset对象都有一个ActiveConnection属性,该属性用来引用Connection对象。

   Command对象:被用来处理重复执行的查询,或处理需要检查在存储过程调用中的输出或返回参数的值的查询。

   Recordset对象:被用来获取数据。Recordset对象存放查询的结果,这些结果由数据的行(成为记录)和列(称为字段)组成。每一列都存放在Recordset的Fields集合中的一个Fields对象中。

6 连接数据库

try {
		pMyConnect.CreateInstance(__uuidof(Connection));
	    pRecord.CreateInstance(__uuidof(Recordset));
		pMyConnect->Open("Provider=SQLOLEDB;Server=192.168.100.200;Database=TEST;uid=sa;pwd=123", "", "", adModeUnknown);
		
		}
	catch (_com_error& e)
	{
		
		AfxMessageBox(e.Description()+e.HelpFile());
		return;
	}

这里创建了连接实例和记录集实例,并用调用了Open函数打开了数据库

第一个参数:连接字符串。这个字符串可以自己获取。在电脑的任意位置文本,并修改为以udl后缀的文件。

VS2019 C++ SQL Server 数据库连接,第1张     VS2019 C++ SQL Server 数据库连接,第2张 

双击打开后 输入自己想要连接的数据库信息,然后再改回文本文件双击打开,第二行就是我们需要的内容了,下面随便给大家做个示例。如果想要显示密码那么就需要选中允许保存密码选项。

VS2019 C++ SQL Server 数据库连接,第3张VS2019 C++ SQL Server 数据库连接,第4张

 后面几个参数

adModeUnknown:缺省。当前的许可权未设置

adModeRead:只读

adModeWrite:只写

adModeReadWrite:可以读写

adModeShareDenyRead:阻止其它Connection对象以读权限打开连接

adModeShareDenyWrite:阻止其它Connection对象以写权限打开连接

adModeShareExclusive:阻止其它Connection对象以读写权限打开连接

adModeShareDenyNone:阻止其它Connection对象以任何权限打开连接

Connection对象除Open()方法外还有许多方法,我们先介绍Connection对象中两个有用的属性ConnectionTimeOut与State。ConnectionTimeOut用来设置连接的超时时间,需要在Open之前调用,例如:

m_pConnection->ConnectionTimeout = 5;///设置超时时间为5秒 m_pConnection->Open("Data Source=adotest;","","",adModeUnknown);

 7 进行数据库操作

因为我是MFC程序,所以我就直接上完整代码了

VS2019 C++ SQL Server 数据库连接,第5张

//连接数据库
void CADOMFCDlg::OnBnClickedConnectButton()
{
	HRESULT hr=CoInitialize(NULL);//ADO是COM组件,所以使用ADO连接数据库需要初始化COM组件
	ASSERT(SUCCEEDED(hr)); //SUCCEEDED(hr)先判断COM是否初始化成功,如果不成功ASSERT的参数即为假,则会报告错误并终止程序
	//_ConnectionPtr p(__uuidof(Connection));
	//pMyConnect = p;
	//_RecordsetPtr pRst(__uuidof(Recordset));//定义记录集对象并实例化对象 
	//pRecord = pRst;
	
	try {
		pMyConnect.CreateInstance(__uuidof(Connection));
	    pRecord.CreateInstance(__uuidof(Recordset));
		pMyConnect->Open("Provider=SQLOLEDB;Server=192.168.100.200;Database=TEST;uid=sa;pwd=123", "", "", adModeUnknown);
		
		}
	catch (_com_error& e)
	{
		
		AfxMessageBox(e.Description()+e.HelpFile());
		return;
	}
	 TODO:  在此添加额外的初始化代码
	CRect rect;
	//showList.DeleteAllItems();
	// 获取编程语言列表视图控件的位置和大小
	showMessage.GetClientRect(rect);
	 为列表视图控件添加全行选中和栅格风格   
	showMessage.SetExtendedStyle(showMessage.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
	// 为列表视图控件添加三列   
	showMessage.InsertColumn(0, _T("ID"), LVCFMT_CENTER, rect.Width() / 5);
	showMessage.InsertColumn(1, _T("姓名"), LVCFMT_CENTER, rect.Width() / 5);
	showMessage.InsertColumn(2, _T("性别"), LVCFMT_CENTER, rect.Width() / 5);
	showMessage.InsertColumn(3, _T("年龄"), LVCFMT_CENTER, rect.Width() / 5);
	showMessage.InsertColumn(4, _T("地址"), LVCFMT_CENTER, rect.Width() / 5);
	connectButton.EnableWindow(0);
	disConnectButton.EnableWindow(1);
	AfxMessageBox(L"数据库连接成功");
}

 VS2019 C++ SQL Server 数据库连接,第6张

void CADOMFCDlg::OnBnClickedCloseButton()
{
	try
	{
		
		pMyConnect->Close();//关闭数据库
		pRecord.Release(); //释放记录集对象指针
		pMyConnect.Release();//释放连接对象指针
		pMyConnect = NULL;
		pRecord = NULL;
		::CoUninitialize();
	}
	catch (_com_error& e)
	{
		/*wchar_t buf[128];
		swprintf(buf, L"错误描述:%s  错误帮助:%s", e.Description(), e.HelpFile());
		AfxMessageBox(buf);*/
		AfxMessageBox(e.Description() + e.HelpFile());
		return;
	}
	
	connectButton.EnableWindow(1);
	disConnectButton.EnableWindow(0);
	AfxMessageBox(L"数据库关闭成功");
}

VS2019 C++ SQL Server 数据库连接,第7张

void CADOMFCDlg::OnBnClickedInsertButton()
{
	// TODO: 在此添加控件通知处理程序代码
	CString insertSql,ID,Name,Sex,Age,Address;
    GetDlgItemText(IDC_ID_EDIT,ID);
	if (ID == "")
	{
		AfxMessageBox(L"请输入ID");
		return;
	}
	GetDlgItemText(IDC_NAME_EDIT, Name);
	GetDlgItemText(IDC_AGE_EDIT, Age);
	GetDlgItemText(IDC_ADDRESS_EDIT, Address);
	if (((CButton*)GetDlgItem(IDC_RADIO1))->GetCheck())
		Sex = "man";
	if (((CButton*)GetDlgItem(IDC_RADIO2))->GetCheck())
		Sex = "woman";
	int id = _ttoi(ID);
	int age = _ttoi(Age);
	insertSql.Format(L"insert into Student values('%d','%s','%s','%d','%s')",id, Name, Sex, age, Address);
	_RecordsetPtr pRecord;
	try {
		pRecord = pMyConnect->Execute((_bstr_t)insertSql, NULL, adCmdText);
	}
	catch (_com_error e) {
		AfxMessageBox(e.ErrorMessage());
		return;
	}
	AfxMessageBox(L"插入数据成功");
    
}

 VS2019 C++ SQL Server 数据库连接,第8张

 这里我的变更条件就是ID name sex age address 可选项,目前只写了一个ID 的。

//更新数据
void CADOMFCDlg::OnBnClickedUpdateButton()
{
	// TODO: 在此添加控件通知处理程序代码
	if (pMyConnect == NULL)
	{
		AfxMessageBox(L"请先连接数据库后再操作");
		return;
	}
	
	int nIndex = condition.GetCurSel();
	CString con;
	CString ID, Name, Sex, Age, Address;
	condition.GetLBText(nIndex, con);
	//获取 需要修改数据
	GetDlgItemText(IDC_ID_EDIT, ID);
	GetDlgItemText(IDC_NAME_EDIT, Name);
	GetDlgItemText(IDC_AGE_EDIT, Age);
	GetDlgItemText(IDC_ADDRESS_EDIT, Address);
	if (((CButton*)GetDlgItem(IDC_RADIO1))->GetCheck())
		Sex = "man";
	else if (((CButton*)GetDlgItem(IDC_RADIO2))->GetCheck())
		Sex = "woman";
	else Sex = "";
	int id = _ttoi(ID);
	int age = _ttoi(Age);
	//获取 需要修改的条件
	if (con == "ID")
	{
		CString updateSql1, updateSql2,updateSql3,updateSql4;
		if (ID == "")
		{
			AfxMessageBox(L"无法对空ID作出更新");
			return;
		}
		else if ( Name == "" && Age == "" && Sex == "" && Address == "")
		{
			AfxMessageBox(L"未作出任何修改");
			return;
		}
		CString selectID;
		selectID.Format(L"select * from Student where ID='%d'", _ttoi(ID));
		try {
			pRecord = pMyConnect->Execute((_bstr_t)selectID, NULL, adCmdText);
		}
		catch (_com_error e) {
			AfxMessageBox(e.ErrorMessage());
			return;
		}
		if (pRecord->adoEOF)
		{
			AfxMessageBox(L"无法对不存在的ID作出修改");
			return;
		}
		if (Name != "")
		{
			updateSql1.Format(L"update Student set name ='%s' where ID='%d'", Name, _ttoi(ID));
			try {
				pRecord = pMyConnect->Execute((_bstr_t)updateSql1, NULL, adCmdText);
			}
			catch (_com_error e) {
				AfxMessageBox(e.ErrorMessage());
				return;
			}
		}
		if (Sex != "")
		{
			updateSql2.Format(L"update Student set sex ='%s' where ID='%d'", Sex, _ttoi(ID));
			try {
				pRecord = pMyConnect->Execute((_bstr_t)updateSql2, NULL, adCmdText);
			}
			catch (_com_error e) {
				AfxMessageBox(e.ErrorMessage());
				return;
			}
		}
		if (Age != "")	
		{   updateSql3.Format(L"update Student set age ='%s' where ID='%d'", Age, _ttoi(ID));
			try {
				pRecord = pMyConnect->Execute((_bstr_t)updateSql3, NULL, adCmdText);
			}
			catch (_com_error e) {
				AfxMessageBox(e.ErrorMessage());
				return;
			}
		}
		if (Address != "")
		{
			updateSql4.Format(L"update Student set address ='%s' where ID='%d'", Address, _ttoi(ID));
			try {
				pRecord = pMyConnect->Execute((_bstr_t)updateSql4, NULL, adCmdText);
			}
			catch (_com_error e) {
				AfxMessageBox(e.ErrorMessage());
				return;
			}
		}
		AfxMessageBox(L"根据ID数据修改成功");
	}
}

 VS2019 C++ SQL Server 数据库连接,第9张

//删除数据
void CADOMFCDlg::OnBnClickedDeleteButton()
{
	// TODO: 在此添加控件通知处理程序代码
	if (pMyConnect == NULL)
	{
		AfxMessageBox(L"请先连接数据库后再操作");
		return;
	}
	CString deleteSql,ID;
	GetDlgItemText(IDC_ID_EDIT, ID);
	if (ID =="")
	{
		AfxMessageBox(L"请输入ID后重试");
		return;
	}
	CString selectID;
	selectID.Format(L"select * from Student where ID='%d'", _ttoi(ID));
	try {
		pRecord = pMyConnect->Execute((_bstr_t)selectID, NULL, adCmdText);
	}
	catch (_com_error e) {
		AfxMessageBox(e.ErrorMessage());
		return;
	}
	if (pRecord->adoEOF)
	{
		AfxMessageBox(L"无法删除不存在的ID");
		return;
	}
	deleteSql.Format(L"delete from Student where ID ='%d'", _ttoi(ID));
	try {
		pRecord = pMyConnect->Execute((_bstr_t)deleteSql, NULL, adCmdText);
	}
	catch (_com_error e) {
		AfxMessageBox(e.ErrorMessage());
		return;
	}
	AfxMessageBox(L"数据删除成功");
}

VS2019 C++ SQL Server 数据库连接,第10张

//查询
void CADOMFCDlg::OnBnClickedSelectButton()
{
	showMessage.DeleteAllItems();
	_bstr_t sqlSelect = "select * from Student";    //查询Studnet表里的数据
	if (pMyConnect == NULL)
	{
		AfxMessageBox(L"请连接数据库后再操作");
		return;
	}
	int i = 0;
	wchar_t* id, * name, * sex, * address, * age;
	pRecord->Open(sqlSelect, pMyConnect.GetInterfacePtr(), adOpenDynamic, adLockOptimistic, adCmdText);
	//pRecord = pMyConnect->Execute(sqlSelect , NULL, adCmdText);   //获取记录集
	
	if (!pRecord->BOF)
		pRecord->MoveFirst();
	else
	{
		AfxMessageBox(L"数据表为空");
		return;
	}
	while (!pRecord->adoEOF)
	{
		id = (wchar_t*)(_bstr_t)pRecord->GetCollect(_T("ID"));
		name = (wchar_t*)(_bstr_t)pRecord->GetCollect(_T("name"));    
		sex = (wchar_t*)(_bstr_t)pRecord->GetCollect(_T("sex"));   
		age = (wchar_t*)(_bstr_t)pRecord->GetCollect(_T("age"));
        address = (wchar_t*)(_bstr_t)pRecord->GetCollect(_T("address"));
		showMessage.InsertItem(i, _T(""));
		showMessage.SetItemText(i, 0, id);     //添加到list Control 中的第i行的第一列下
	    showMessage.SetItemText(i, 1, name);     //添加到list Control 中的第i行的第二列下
		showMessage.SetItemText(i, 2, sex);     //添加到list Control 中的第i行的第三列下
		showMessage.SetItemText(i, 3, age);     //添加到list Control 中的第i行的第四列下
		showMessage.SetItemText(i, 4, address);
		i += 1;   //下次循环指向第2行
		pRecord->MoveNext();   //移动到下一个记录
	}
	pRecord->Close();
	
}

ODBC

1  什么是ODBC

开放数据库连接(ODBC)是微软提出的数据库访问接口标准。用微软管阀的话来说就是:ODBC 是数据库 API 的规范。任何人都可以编写 ODBC 应用程序和驱动程序。使用 ODBC 的应用程序负责任何跨数据库功能。 例如,ODBC 不是异类联接引擎,也不是分布式事务处理器。 但是,由于它是与 DBMS 无关的,因此可用于生成此类跨数据库工具。

2  步骤   

       1 sql Server 创建数据库和表 

       2  ODBC创建数据源

       3  VS配置数据源 

       4  VS连接数据源 (连接数据库)      

       5  VS访问数据源(实现增删改查)

3 sql Server 创建数据库和表

   为了方便测试,我创建了一个最常见的student数据库和student信息表,如下图

     VS2019 C++ SQL Server 数据库连接,第11张

4  创建数据源 

 1  在搜索栏搜索ODBC,这里我选择64位,最好与自己的VS编译版本和sql server版本一致。

VS2019 C++ SQL Server 数据库连接,第12张

2  点击添加

VS2019 C++ SQL Server 数据库连接,第13张

3 找到自己需要的数据库 数据源类型

 VS2019 C++ SQL Server 数据库连接,第14张

4  编辑自己的数据源,这个在代码编写的时候会使用。 ​​​​​服务器填的是自己数据库所在的电脑IP地址,如果是本地就可以设置为127.0.0.1。VS2019 C++ SQL Server 数据库连接,第15张

 5  点击下一页 选择红框选项,并输入自己的sql server配置的sa 和密码

 VS2019 C++ SQL Server 数据库连接,第16张

6  如果配置正确 就会进到下一个页面,默认下一步就好

VS2019 C++ SQL Server 数据库连接,第17张

7 点击测试数据源

VS2019 C++ SQL Server 数据库连接,第18张

8 出现下图就是数据源创建成功了,就可以准备代码了。

VS2019 C++ SQL Server 数据库连接,第19张

刚才说到配置正确才会进到下一步,如果sql server 安装失败或者配置失败就会出现下图类似的错误,至于解决方案,我的建议是重装,至于是装软件还是装机看个人运气。

 VS2019 C++ SQL Server 数据库连接,第20张

5  VS配置数据源

1  vs 连接数据库 工具->连接到数据库

VS2019 C++ SQL Server 数据库连接,第21张

 2   如果是第一次使用 会弹出安装插件的提示,点击确定

VS2019 C++ SQL Server 数据库连接,第22张

 3    单击安装

VS2019 C++ SQL Server 数据库连接,第23张

 4  安装完成 重复步骤一

 VS2019 C++ SQL Server 数据库连接,第24张

 5  连接设置

选择SQL Server身份验证并输入自己设定的sa和密码。下方的数据库名可以选择自己创建的数据库,

这样在应用中就默认打开我们设置的数据库,也可以不设置。

VS2019 C++ SQL Server 数据库连接,第25张

配置成功后就可以在服务资源管理器中看到我们的数据库和表了

 VS2019 C++ SQL Server 数据库连接,第26张

 6  vs 连接数据源

这里要知道一些概念和操作

1  句柄 

本部分参考 以下前辈的博客(162条消息) 【ODBC】ODBC连接数据库详细说明_yzzheng_60125的博客-CSDN博客

应用程序运行后,为维护执行的状态,ODBC 管理器和ODBC 驱动程序中必须保持足够的控制信息。应用程序要求ODBC 管理器和ODBC 驱动程序为ODBC环境、每个连接以及每个SQL语句分配描述/控制信息存储空间,并返回指向各个存储区的句柄供其使用。

  (1)环境句柄:整个ODBC上下文的根句柄。标识全程数据访问控制信息的内存结构,包括有效连接句柄以及当前活动连接句柄。ODBC将环境句柄定义为HENV类型的变量。应用程序使用单一的环境句柄,在连接到数据源以前必须申请该句柄。

  (2)连接句柄:管理有关数据库会话的所有信息。连接句柄标识每个特定的连接信息的内存结构。ODBC将环境句柄定义为HDBC类型的变量。应用程序在连接数据源之前申请连接句柄。每个连接句柄与环境句柄有关,环境句柄上可以有多个与其有关的连接句柄。

  (3)语句句柄:ODBC语句包括应用访问数据源的SQL语句和语句相关的管理信息,语句句柄标识每个语句管理信息的内存结构。ODBC将语句句柄定义为HSTMT类型的变量。应用程序在提交SQL请求之前也必须申请语句句柄。每个语句句柄与一个连接句柄有关,每个连接句柄上可以有多个与其有关的语句句柄。

这里大家也应该明白了,一个应用程序有一个环境句柄,用来给ODBC提供环境支持,在这个环境句柄上有多个连接句柄,用来管理数据库和标识数据库结构,每个环境句柄上又有多个语句句柄可以访问数据源。可以这样理解,但是肯定有偏差。

ODBC通过SQLAllocHandle来分配句柄

    SQLRETURN ret;//返回信息

    SQLHENV henv;//环境句柄  可以理解为申请并配置ODBC环境

    SQLHDBC hdbc;//连接句柄  可以理解为申请数据库连接

    SQLHSTMT hstmt;//语句句柄  可以理解为执行sql语句访问数据源

环境句柄SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv)
连接句柄SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc)
语句句柄SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt)或者SQLAllocStmt(hdbc, hstmt)

2  连接数据库

    SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv);//申请环境句柄
	SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);//设置环境句柄
	SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);//申请数据库连接
	ret = SQLConnect(hdbc, L"test", SQL_NTS, L"sa", SQL_NTS, L"你自己设置的密码", SQL_NTS);
	if ((ret == SQL_SUCCESS) || (ret == SQL_SUCCESS_WITH_INFO))
	{
		AfxMessageBox(L"数据库成功打开");
		
	}
	else
	{
		AfxMessageBox(L"打开数据库失败");
		
	}

上述代码中SQLConnect 函数的作用就是连接数据源的数据库 

参数含义: hdbc--连接句柄

                test--数据源名称 ,根据个人添加的源名称

                SQL_NTS--数据源名称的长度,用户名长度,密码长度

                sa--用户名,根据个人设置

                "你自己设置的密码"-- sa的密码

字符串前加L的含义:

文章参考前辈博客

字符串 前 L的含义_字符串前加l_whz_zb的博客-CSDN博客

一、 在字符串前加一个L作用: unicode字符集是两个字节组成的。L告示编译器使用两个字节的 unicode 字符集。

   如  L"我的字符串"    表示将ANSI字符串转换成unicode的字符串,就是每个字符占用两个字节。

  strlen("asd")   =   3;   

  strlen(L"asd")   =   6;

 二、  _T宏可以把一个引号引起来的字符串,根据你的环境设置,使得编译器会根据编译目标环境选择合适的(Unicode还是ANSI)字符处理方式

   如果你定义了UNICODE,那么_T宏会把字符串前面加一个L。这时 _T("ABCD") 相当于 L"ABCD" ,这是宽字符串。 

   如果没有定义,那么_T宏不会在字符串前面加那个L,_T("ABCD") 就等价于 "ABCD"

 

 返回值:系统宏定义的防护值 这俩种情况就是成功

         VS2019 C++ SQL Server 数据库连接,第27张

 3  关闭数据库

我个人认为释放句柄是有顺序的,因为一对多的关系,所以要反着释放,当然只是我个人的认为

释放语句句柄SQLFreeHandle(SQL_HANDLE_STMT, hstmt)或者SQLFreeStmt(hstmt, SQL_DROP)
释放连接句柄SQLFreeHandle(SQL_HANDLE_DBC, hdbc)或者SQLDisconnect(hdbc)与SQLFreeConnect(hdbc)
释放环境句柄SQLFreeHandle(SQL_HANDLE_ENV, henv)或者SQLFreeEnv(henv)

 7  VS访问数据源(实现增删改查)

1 增加

    //一些变量的定义在前文已经说明了
    ret = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);//申请语句句柄
    CString str1  = L"use student";//use 【你的数据库名称】
	CString str2 = L"insert into Student values ('1','Bob', '18', '男', 'China')";//要执行的SQL语句
	
	ret = SQLExecDirect(hstmt, (SQLWCHAR *)str1.GetBuffer(), SQL_NTS);
	ret = SQLExecDirect(hstmt, (SQLWCHAR*)str2.GetBuffer(), SQL_NTS);
	if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
		AfxMessageBox(L"插入数据成功");
	}
	else {
		AfxMessageBox(L"插入数据失败");
	}

效果 

VS2019 C++ SQL Server 数据库连接,第28张

 2 删除

ret = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);//申请句柄
	CString str1 = L"use student";
	CString str2 = L"delete from student where name ='Bob'";
	ret = SQLExecDirect(hstmt, (SQLWCHAR*)str1.GetBuffer(), SQL_NTS);
	ret = SQLExecDirect(hstmt, (SQLWCHAR*)str2.GetBuffer(), SQL_NTS);
	if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
		AfxMessageBox(L"数据删除成功");
	}
	else {
		AfxMessageBox(L"数据删除失败,请检查表中是否有此数据");
	}

VS2019 C++ SQL Server 数据库连接,第29张

 3 修改

    ret = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);//申请句柄
	CString str1 = L"use studnet";
	CString str2 = L"update student  set address ='济南' where name ='Bob'";
	ret = SQLExecDirect(hstmt, (SQLWCHAR*)str1.GetBuffer(), SQL_NTS);
	ret = SQLExecDirect(hstmt, (SQLWCHAR*)str2.GetBuffer(), SQL_NTS);
	if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
		AfxMessageBox(L"数据修改成功");
	}
	else {
		AfxMessageBox(L"数据删除失败,请检查表中是否有此数据");
	}

VS2019 C++ SQL Server 数据库连接,第30张

 4  查找

引用部分以下前辈博客内容

(162条消息) 【ODBC】ODBC连接数据库详细说明_yzzheng_60125的博客-CSDN博客

SQLFetch函数的功能是将结果集的当前记录指针移至下一个记录;

SQLGetData函数的功能是提取结果集中当前记录的某个字段值。通常可以采用一个循环以提取结果集中所有记录的所有字段值,该循环重复执行SQLFetch和SQLGetData函数,直至SQLFetch函数返回SQL_NO_DATA_FOUND, 这表示已经到达结果集的末尾。

因为我用的是MFC程序 ,用到了 List control 控件 我的查询结果放在了这个控件上

    message_list.DeleteAllItems(); 
	ret = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);//申请句柄
	CString str1 = L"use student";
	CString str2 = L"select * from student";
	ret = SQLExecDirect(hstmt, (SQLWCHAR*)str1.GetBuffer(), SQL_NTS);
	ret = SQLExecDirect(hstmt, (SQLWCHAR*)str2.GetBuffer(), SQL_NTS);
	
	int i = 0;
	if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
		while (SQLFetch(hstmt) != SQL_NO_DATA)
		{
			SQLWCHAR str1[10], str2[12], str3[20], str4[20],str5[20];
			SQLLEN len_str1, len_str2, len_str3, len_str4,len_str5;
			SQLGetData(hstmt, 1, SQL_C_CHAR, str1, 10, &len_str1);   //获取第一列数据
			SQLGetData(hstmt, 2, SQL_C_CHAR, str2, 12, &len_str2);
			SQLGetData(hstmt, 3, SQL_C_CHAR, str3, 20, &len_str3);
			SQLGetData(hstmt, 4, SQL_C_CHAR, str4, 20, &len_str4);
			SQLGetData(hstmt, 5, SQL_C_CHAR, str5, 20, &len_str5);
			message_list.InsertItem(i, _T(""));
			message_list.SetItemText(i, 0, str1);     //添加到list Control 中的第i行的第一列下
			message_list.SetItemText(i, 1, str2);     //添加到list Control 中的第i行的第二列下
			message_list.SetItemText(i, 2, str3);     //添加到list Control 中的第i行的第三列下
			message_list.SetItemText(i, 3, str4);     //添加到list Control 中的第i行的第四列下
			message_list.SetItemText(i, 4, str5);     //添加到list Control 中的第i行的第五列下
			i++;
			
		}
		
	}
	else {
		AfxMessageBox(L"数据删除失败,请检查表中是否有此数据");
	}

MFC部分代码

void CtestSqlServerDlg::OnBnClickedButton1() 
{
	// TODO: 在此添加控件通知处理程序代码
	SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv);//申请环境句柄
	SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);//设置环境句柄
	SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);//申请数据库连接
	ret = SQLConnect(hdbc, L"test", SQL_NTS, L"sa", SQL_NTS, L"zyzb1234!", SQL_NTS);
	if ((ret == SQL_SUCCESS) || (ret == SQL_SUCCESS_WITH_INFO))
	{
		AfxMessageBox(L"数据库成功打开");
		//用于获取list control的尺寸
		CRect rect;
		//获取控件尺寸
		message_list.GetClientRect(rect);
		//风格设计
		message_list.SetExtendedStyle(message_list.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
		//创建表格字段
		message_list.InsertColumn(0, _T("ID"), LVCFMT_CENTER, rect.Width() / 5, 0);
		message_list.InsertColumn(1, _T("姓名"), LVCFMT_CENTER, rect.Width() / 5, 1);
		message_list.InsertColumn(2, _T("性别"), LVCFMT_CENTER, rect.Width() / 5, 2);
		message_list.InsertColumn(3, _T("年龄"), LVCFMT_CENTER, rect.Width() / 5, 3);
		message_list.InsertColumn(4, _T("地址"), LVCFMT_CENTER, rect.Width() / 5, 4);
		btn_connect.EnableWindow(0);
		btn_disconnect.EnableWindow(1);
	}
	else
	{
		AfxMessageBox(L"打开数据库失败");
		
	}
	
	
	
	
	
	/*if (!my_dataBase.Open(NULL, FALSE, FALSE, L"ODBC;DSN=test;UID=sa;PWD=123"))
	{
		AfxMessageBox(L"打开数据库失败");
	}
	else
	{
		AfxMessageBox(L"数据库成功打开");
	}*/
}
//插入数据
void CtestSqlServerDlg::OnBnClickedButton2() 
{
	// TODO: 在此添加控件通知处理程序代码
   
	
	ret = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);//申请语句句柄
    CString str1  = L"use student";//use 【你的数据库名称】
	CString str2 = L"insert into Student values ('1','Bob', '18', '男', 'China')";//要执行的SQL语句
	
	ret = SQLExecDirect(hstmt, (SQLWCHAR *)str1.GetBuffer(), SQL_NTS);
	ret = SQLExecDirect(hstmt, (SQLWCHAR*)str2.GetBuffer(), SQL_NTS);
	if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
		AfxMessageBox(L"插入数据成功");
	}
	else {
		AfxMessageBox(L"插入数据失败");
	}
	
	
	
	
	/* CString insertSQL = L"insert into Student values ('1','zhangsan','man','18','China')";
	my_dataBase.ExecuteSQL(insertSQL);
	*/
}
//删除数据
void CtestSqlServerDlg::OnBnClickedButton4()
{
	ret = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);//申请句柄
	CString str1 = L"use student";
	CString str2 = L"delete from student where name ='Bob'";
	ret = SQLExecDirect(hstmt, (SQLWCHAR*)str1.GetBuffer(), SQL_NTS);
	ret = SQLExecDirect(hstmt, (SQLWCHAR*)str2.GetBuffer(), SQL_NTS);
	if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
		AfxMessageBox(L"数据删除成功");
	}
	else {
		AfxMessageBox(L"数据删除失败,请检查表中是否有此数据");
	}
	
	
}
//修改数据
void CtestSqlServerDlg::OnBnClickedButton3()
{
	
	ret = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);//申请句柄
	CString str1 = L"use studnet";
	CString str2 = L"update student  set address ='济南' where name ='Bob'";
	ret = SQLExecDirect(hstmt, (SQLWCHAR*)str1.GetBuffer(), SQL_NTS);
	ret = SQLExecDirect(hstmt, (SQLWCHAR*)str2.GetBuffer(), SQL_NTS);
	if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
		AfxMessageBox(L"数据修改成功");
	}
	else {
		AfxMessageBox(L"数据删除失败,请检查表中是否有此数据");
	}
}
//查询数据
void CtestSqlServerDlg::OnBnClickedButton5()
{   
	message_list.DeleteAllItems();
	ret = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);//申请句柄
	CString str1 = L"use student";
	CString str2 = L"select * from student";
	ret = SQLExecDirect(hstmt, (SQLWCHAR*)str1.GetBuffer(), SQL_NTS);
	ret = SQLExecDirect(hstmt, (SQLWCHAR*)str2.GetBuffer(), SQL_NTS);
	
	int i = 0;
	if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
		while (SQLFetch(hstmt) != SQL_NO_DATA)
		{
			SQLWCHAR str1[10], str2[12], str3[20], str4[20],str5[20];
			SQLLEN len_str1, len_str2, len_str3, len_str4,len_str5;
			SQLGetData(hstmt, 1, SQL_C_CHAR, str1, 10, &len_str1);   //获取第一列数据
			SQLGetData(hstmt, 2, SQL_C_CHAR, str2, 12, &len_str2);
			SQLGetData(hstmt, 3, SQL_C_CHAR, str3, 20, &len_str3);
			SQLGetData(hstmt, 4, SQL_C_CHAR, str4, 20, &len_str4);
			SQLGetData(hstmt, 5, SQL_C_CHAR, str5, 20, &len_str5);
			message_list.InsertItem(i, _T(""));
			message_list.SetItemText(i, 0, str1);     //添加到list Control 中的第i行的第一列下
			message_list.SetItemText(i, 1, str2);     //添加到list Control 中的第i行的第二列下
			message_list.SetItemText(i, 2, str3);     //添加到list Control 中的第i行的第三列下
			message_list.SetItemText(i, 3, str4);     //添加到list Control 中的第i行的第四列下
			message_list.SetItemText(i, 4, str5);     //添加到list Control 中的第i行的第五列下
			i++;
			
		}
		
	}
	else {
		AfxMessageBox(L"数据删除失败,请检查表中是否有此数据");
	}
}
//断开连接
void CtestSqlServerDlg::OnBnClickedButton6()
{
	// TODO: 在此添加控件通知处理程序代码
	//my_dataBase.Close();
	SQLFreeHandle(SQL_HANDLE_STMT, hstmt);//释放语句
	SQLFreeHandle(SQL_HANDLE_DBC, hdbc);//释放连接
	SQLFreeHandle(SQL_HANDLE_ENV, henv);//释放环境
	AfxMessageBox(L"数据库关闭成功");
	/*btn_connect.EnableWindow(1);
	btn_disconnect.EnableWindow(0);*/
}