C++ CRUD programming for DB

发布于:2024-04-17 ⋅ 阅读:(16) ⋅ 点赞:(0)

1、ODBC

        开放数据库互连,微软主导的关系型数据库接口标准,允许同一代码访问不同DBMS中的数据。小案例:C++连接Access数据库----增删改查_c++ access数据库-CSDN博客

·ODBC(Open Database Connectivity,开放数据库连接)

        ODBC是Microsoft公司为应用程序访问关系型数据库时提供的一组标准接口规范。ODBC对不同的关系型数据库提供了统一的API,使用该API来访问任何提供了ODBC驱动程序的数据库。

··ODBC的构成

··ODBC的体系结构

        其中,应用程序是我们自己写的,通过API接口,调用驱动程序管理器,各类关系型数据库需提供自己的ODBC驱动程序。

··数据库ODBC驱动管理与安装

        在 运行 中输入 odbcad32 可启动 odbc数据源管理器,如果没有需要的数据库驱动,需自行下载安装。安装ODBC方法-CSDN博客

··ODBC的优点

··ODBC自研

ODBC微软官方参考文档:Microsoft ODBC Driver for SQL Server - ODBC Driver for SQL Server | Microsoft Learn

·ODBC编程的步骤

··配置数据源

安装ODBC方法-CSDN博客

··代码分步详解

相关头文件

//ODBC操作数据库需要包含的头文件
#include <windows.h>
#include <sqlext.h>
#include <sqltypes.h>
#include <sql.h>

代码主体
1、初始化环境句柄
//############################### Step  1 ####################
// 1、分配ODBC环境句柄
SQLHANDLE hEnv = NULL;   // 环境句柄
if (SQL_ERROR == SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv)) {
	std::cout << "分配环境句柄失败!\n";
	return -1;
}
// 2、设定ODBC版本
if (SQL_ERROR == SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER)) {
	std::cout << "ODBC版本设定失败!\n";
	return -1;
}
2、初始化连接句柄
//############################### Step  2 ####################
// 1、分配连接句柄(应用程序可以连接到数据源或驱动程序之前,它必须分配一个连接句柄
SQLHANDLE hDbc = NULL;   // 数据库连接句柄
// 2、初始化数据库连接句柄
SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc);
3、连接数据库
	//############################### Step  3 ####################
  	// 1、根据DSN连接连接数据库
		//法1
 // 	SQLCHAR* connStr = (SQLCHAR*)"DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=D:\\MyAccess\\test.accdb"; //数据库路径
 // 	SQLRETURN ret = SQLDriverConnect(hDbc, NULL, connStr, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);
	//if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
	//	std::cout << "数据库连接成功!" << std::endl;
	//}

		//法2
	/*
	SQLRETURN ret = SQLConnect(hDbc, (SQLTCHAR*)_T("数据源名称"), SQL_NTS,
		(SQLTCHAR*)_T("用户名"), SQL_NTS,
		(SQLTCHAR*)_T("密码"), SQL_NTS);
	*/
	SQLRETURN ret = SQLConnect(hDbc, (SQLTCHAR*)_T("AccessODBC"), SQL_NTS,
		NULL, SQL_NTS,
		NULL, SQL_NTS);
	if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
		std::cout << "数据库连接成功!" << std::endl;
	}
	else if (ret == SQL_ERROR) {
		SQLTCHAR state[128] = { 0 };
		SQLTCHAR msg[128] = { 0 };
		//获取错误信息(参数 连接句柄,环境句柄,语句句柄...)
		ret = SQLError(hEnv, hDbc, NULL, state, NULL, msg, sizeof(msg), NULL);
		_tprintf(_T("%s %s\n"), state, msg);
	}
4、初始化语句句柄
//############################### Step  4 ####################
// 插入数据

// 分配语句句柄
SQLHSTMT hStmt = NULL;
ret = SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);
···执行INSERT语句(几种写法) 
//准备SQL语句
SQLTCHAR sql[] = _T("INSERT INTO 通讯录 VALUES(10,'凌敏','女','21','131422','China')"); //无“?”版本
ret = SQLPrepare(hStmt, sql, SQL_NTS);

//执行SQL语句
ret = SQLExecute(hStmt);

注意:以上SQL语句的写法,INSERT INTO 通讯录 VALUES(10,'凌敏','女','21','131422','China'),需要写出每一个属性的值,INSERT INTO 通讯录 VALUES(10,'凌敏','女','21','131422')×。

下面价绍一种复杂版的SQL语句写法

SQLTCHAR sql[] = _T("INSERT INTO 通讯录 VALUES(?,?,?,?,?,?)"); //复杂版
// 复杂版下的参数传递
//6个参数
SQLINTEGER id = 124;
SQLTCHAR name[32] = _T("lvtu");
SQLTCHAR sex[32] = _T("男");
SQLTCHAR age[32] = _T("25");
SQLTCHAR num[32] = _T("3123124");
SQLTCHAR country[32] = _T("CHINA");

//绑定SQL语句的参数
ret = SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_SHORT, SQL_SMALLINT, 0, 0, (SQLPOINTER)&id, 0, NULL);
ret = SQLBindParameter(hStmt, 2, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR, sizeof(name), 0, (SQLPOINTER)name, 0, NULL);
ret = SQLBindParameter(hStmt, 3, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR, sizeof(sex), 0, (SQLPOINTER)sex, 0, NULL);
ret = SQLBindParameter(hStmt, 4, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR, sizeof(age), 0, (SQLPOINTER)age, 0, NULL);
ret = SQLBindParameter(hStmt, 5, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR,  sizeof(num), 0,(SQLPOINTER)num, 0, NULL);
ret = SQLBindParameter(hStmt, 6, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR,  sizeof(country), 0,(SQLPOINTER)country, 0, NULL);
ret = SQLPrepare(hStmt, sql, SQL_NTS);

其中,SQLBindParameter参数介绍

以上用到了两种执行语句,分别是:SQLExecute和SQLExecDirect,区别在于:前者用来执行由SQLPrepare创建的的sql语句,后者直接执行,语句在运行时被编译并执行。

SQLExecute是执行已准备的SQL语句,SQLExecDirect是提交SQL语句一次执行的最快方法。

···执行SELECT语句

步骤为:分配语句句柄-》准备SQL语句-》执行SQL语句-》绑定结果集的列(很重要!)-》遍历查询结果-》查看被查询的行数

//############################### Step  5 ####################
// 查询数据

// 分配语句句柄
SQLHSTMT hStmt = NULL;
ret = SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);

//准备SQL语句
SQLTCHAR sql[] = _T("SELECT * FROM 通讯录");

//执行SQL语句
ret = SQLExecDirect(hStmt, sql, SQL_NTS);  //SL_NTS可以自动计算sql的长度
if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
	std::wcout << _T("数据库查询成功!") << std::endl;

	//查询之后,所有数据放到了一块缓冲区,我们需要把他分离出来
	INT ID = 0;
	TCHAR name[32] = { 0 };
	TCHAR sex[10] = { 0 };
	TCHAR age[10] = { 0 };
	TCHAR phone[32] = { 0 };
	TCHAR country[32] = { 0 };

	SQLLEN len = SQL_NTS;
	SQLBindCol(hStmt, 1, SQL_C_SHORT, &ID, sizeof(ID), 0);
	SQLBindCol(hStmt, 2, SQL_C_WCHAR, name, sizeof(name), &len);
	SQLBindCol(hStmt, 3, SQL_C_WCHAR, sex, sizeof(sex), &len);
	SQLBindCol(hStmt, 4, SQL_C_WCHAR, age, sizeof(age), &len);
	SQLBindCol(hStmt, 5, SQL_C_WCHAR, phone, sizeof(phone), &len);
	SQLBindCol(hStmt, 6, SQL_C_WCHAR, country, sizeof(country), &len);

	//逐行遍历,获取数据
	ret = SQLFetch(hStmt);
	SHORT count = 0;
	while (ret != SQL_NO_DATA) {
		++count;
		std::wcout << ID << "\t" << name << "\t" << sex << "\t"
			<< age << "\t" << phone << "\t" << country << "\t" << std::endl;

		//每次清除一下上行旧数据,保证下次获取的数据干净
		ID = 0;
		ZeroMemory(name, sizeof(name));
		ZeroMemory(sex, sizeof(sex));
		ZeroMemory(age, sizeof(age));
		ZeroMemory(phone, sizeof(phone));
		ZeroMemory(country, sizeof(country));

		//获取下一行数据,填充到 ID ,Name......
		ret = SQLFetch(hStmt);
	}

看一下一共查了几行
//SQLLEN n = 0;
//ret = SQLRowCount(hStmt, &n);  //查询被影响的行数 
//if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
//	_tprintf(_T("查询%d行数据成功!\n"), n);
//}
_tprintf(_T("查询%d行数据成功!\n"), count);

一般而言,查询被影响的行数可以用SQLRowCount函数,但是

对于查询操作,SQLRowCount可能不会返回预期的结果,特别是在某些数据库管理系统中,它可能返回-1,表示无法确定影响的行数。这是因为在查询操作中,数据库可能不会跟踪或无法确定实际检索的行数,特别是当使用了游标或者在数据量大的情况下。

为了避免这种情况,可以不使用SQLRowCount来获取查询结果的行数。相反,可以使用一个计数器变量在遍历结果集时递增,这样在遍历结束后,计数器中的值就是查询结果的实际行数。

···执行DELETE语句
//############################### Step  6 ####################
//删除操作
//分配语句句柄
SQLHSTMT  hStmt = NULL;
ret = SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);

//SQL删除语句 
//SQLTCHAR sql[] = _T("DELETE FROM  通讯录 WHERE  iID = 2");//简单版本
SQLTCHAR sql[] = _T("DELETE FROM  通讯录 WHERE  iID = ?");//复杂版本

//准备SQL语句
ret = SQLPrepare(hStmt, sql, SQL_NTS);//SQL_NTS自动计算sql语句的长度

//复杂版本绑定SQL语句的参数 
int id = 2;
ret = SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &id, 0, NULL);

//执行SQL语句
ret = SQLExecute(hStmt);
if ((ret == SQL_SUCCESS) || (ret == SQL_SUCCESS_WITH_INFO) || (ret == SQL_NO_DATA))  //SQL_NO_DATA表示受影响行数为0
{
	SQLLEN n = 0;
	ret = SQLRowCount(hStmt, &n);//查询被影响的行数(适用于SELECT ,INSERT,UPDATE,DELETE操作)
	if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
	{
		_tprintf(_T("删除%d行数据成功!\n"), n);
	}

}
else if (ret == SQL_ERROR)
{
	SQLTCHAR   state[128] = { 0 };
	SQLTCHAR   msg[128] = { 0 };
	//获取错误信息,注意填写语句句柄
	ret = SQLError(hEnv, hDbc, hStmt, state, NULL, msg, sizeof(msg), NULL);
	std::wcout << state << "   " << msg << std::endl;
}
 ```执行UPDATE语句
//############################### Step  7 ####################
//修改数据
//分配语句句柄
	SQLHSTMT  hStmt = NULL;
	ret = SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);

	//SQL修改语句 
	//SQLTCHAR sql[] = _T("UPDATE    通讯录 SET  iName='小雪'   WHERE  iSex = '女’");//简单版本
	SQLTCHAR sql[] = _T("UPDATE    通讯录 SET  iName=?  WHERE  iSex = ? ");//复杂版本

	//准备SQL语句
	ret = SQLPrepare(hStmt, sql, SQL_NTS);//SQL_NTS自动计算sql语句的长度

	//复杂版本绑定SQL语句的参数  
	SQLLEN len = SQL_NTS;
	SQLTCHAR  newName[50] = _T("小雪");
	ret = SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR, 50, 0, (SQLPOINTER)newName, 0, &len);

	int id = 1000;
	ret = SQLBindParameter(hStmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &id, 0, NULL);


	//执行SQL语句
	ret = SQLExecute(hStmt);
	if ((ret == SQL_SUCCESS) || (ret == SQL_SUCCESS_WITH_INFO))
	{
		SQLLEN n = 0;
		ret = SQLRowCount(hStmt, &n);//查询被影响的行数(适用于SELECT ,INSERT,UPDATE,DELETE操作)
		if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
		{
			_tprintf(_T("修改%d行数据成功!\n"), n);
		}

	}
	else if (ret == SQL_ERROR)
	{
		SQLTCHAR   state[128] = { 0 };
		SQLTCHAR   msg[128] = { 0 };
		//获取错误信息,注意填写语句句柄
		ret = SQLError(hEnv, hDbc, hStmt, state, NULL, msg, sizeof(msg), NULL);
		std::wcout << state << "   " << msg << std::endl;
	}
···释放资源

注意顺序:语句执行完先释放语句句柄,接着关闭数据库连接,然后释放连接句柄,最后释放环境句柄。

	// 释放句柄
	//释放语句句柄
	if (hStmt) {
		ret = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
	}
	// 关闭数据库连接
	 ret = SQLDisconnect(hDbc);
	 if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
		 std::wcout << _T("数据库关闭成功!") << std::endl;
	 }
	//释放连接句柄
	if (hDbc) {
		ret = SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
	}

	//释放环境句柄
	if (hEnv) {
		ret = SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
	}
代码中错误显示函数介绍 

ODBC连接MySQL数据库(CRUD)

1、创建一个MYSQL数据库

2、下载安装MySQL数据库ODBC驱动和配置MySQL ODBC数据源

下载下载安装MySQL数据库ODBC驱动和配置MySQL ODBC数据源_mysql odbc 8.0 unicode driver 下载-CSDN博客

配置: (本地就填127.0.0.1)

3、代码

/*
SQLRETURN ret = SQLConnect(hDbc, (SQLTCHAR*)_T("数据源名称"), SQL_NTS,
    (SQLTCHAR*)_T("用户名"), SQL_NTS,
    (SQLTCHAR*)_T("密码"), SQL_NTS);
*/
SQLRETURN ret = SQLConnect(hDbc, (SQLTCHAR*)_T("MYSQLODBC"), SQL_NTS,
    (SQLTCHAR*)_T("root"), SQL_NTS,
    (SQLTCHAR*)_T("2410947747"), SQL_NTS);

修改这段。

2、OLE DB

对象连接嵌入数据库,是基于COM规范的低级别、高性能API,仅在Windows上可用。

3、ADO

活动数据对象,ADO向我们提供了一个高层的对OLE DB的封装接口。ADO是对当前微软所支持的数据库进行操作的最简单直接和最有效的方法。