记录一次实验-华科20年秋季数据库实验(小课设)

华科20年秋季数据库实验(小课设)

记一次实验 —— VS2019 + Microsoft SQL Server 2019 + C++

首先,本实验基于你已会基本的数据库操作,SQL语句,准备写一个类似管理系统的小程序,本文会给出基本的C++操作数据库存储数据的方法。

如何安装SQL SERVER不再做介绍,网上应该有很多资料,安装完成后,关键在ODBC源的配置,参考博客https://blog.csdn.net/nanyouWSH/article/details/47681903,它完美的解决了这个问题。然后我们就可以在VS中连接该数据库了。

数据库中的表说明:
记录一次实验-华科20年秋季数据库实验(小课设)_第1张图片

然后我们可以插入一些基础数据,当然为了后面的统计更具体,可以再适当新增一些合理的数据
记录一次实验-华科20年秋季数据库实验(小课设)_第2张图片

本实验需要实现的功能如下:
记录一次实验-华科20年秋季数据库实验(小课设)_第3张图片

很显然我们只需要在main里设置简单的选择菜单,并实现6个函数即可。别看代码1k行,实际上有大量的重复,思想其实是一样的,只需要弄清楚一个函数的实现流程,其余的就是基础C操作。一些关键的地方我已经给出注释,主要就是用户输入相关数据,我们拼接成合适的sql语句执行。功能4,5,6实现了获取sql返回信息的功能,可以在我们主程序中显示出来。所以,建议直接看4,5,6中的某一个,可以大致了解如何在VS主程序中输入信息然后执行sql语句,并回显结果。(或者把结果存储到一些主程序的变量中进行处理再输出)。

至于更加权威的参考示例,见微软官方给出的:

https://docs.microsoft.com/zh-cn/sql/connect/odbc/cpp-code-example-app-connect-access-sql-db?view=azuresqldb-current

代码如下,你可以选择性的看某些函数,只需要弄清楚实现流程即可

/*
每一个ODBC API函数都返回一个代码——返回码,指示函数执行的成功与否。
如果函数调用成功,返回码为SQL_SUCCESS或SQL_SUCCESS_WITH_INFO。
SQL_SUCCESS指示可通过诊断记录获取有关操作的详细信息,
SQL_SUCCESS_WITH_INFO指示应用程序执行结果带有警告信息,可通过诊断记录获取详细的信息。
如果函数调用失败,返回码为SQL_ERROR

一般地,编写ODBC程序主要有以下几个步骤:
1.分配ODBC环境 Open Database Connectivity
2.分配连接句柄
3.连接数据源
4.构造和执行SQL语句
5.取得执行结果
6.断开同数据源的连接
7.释放ODBC环境
*/

#include     
#include 
#include 
#include     
#include 
#include     
#include    
#include     
#include     
#include 
#include 
#include 
#include 
using namespace std;
#define MAXBUFLEN 255

SQLLEN cbSno, cbCno, cbCname, cbCpno, cbSname, cbSsex, cbSdept, cbScholarship,cbSage,cbGrade,cbCcredit;
SQLCHAR szSno[10], szCno[5], szCname[45], szCpno[5], szSname[25], szSsex[3], szSdept[25], szScholarship[5];
SQLSMALLINT szSage, szGrade, szCcredit;

SQLINTEGER sCustID, cbName, cbCustID, cbPhone;  //cb是指向绑定数据列使用的长度的指针.

//后面统计需要
struct Stu {
	int grade;
	int credit;
	string sno;
	Stu(int g, int c, string s) : grade(g), credit(c), sno(s) {}
};

SQLRETURN retcode;
SQLHENV henv = SQL_NULL_HENV; //环境句柄
SQLHDBC hdbc = SQL_NULL_HDBC; //连接句柄
SQLHSTMT hstmt = SQL_NULL_HSTMT; //语句句柄

//处理错误返回时需要
SQLCHAR       SqlState[6], SQLStmt[100], Msg[SQL_MAX_MESSAGE_LENGTH];
SQLINTEGER    NativeError;
SQLSMALLINT   i, MsgLen;
SQLRETURN     rc;
SQLLEN numRecs = 0;

void showmenu()
{
	system("cls");
	printf("-----------------------欢迎----------------------------\n请输入需要执行的操作:\n");
	printf("1.学生信息维护\n");
	printf("2.课程信息维护\n");
	printf("3.学生成绩维护\n");
	printf("4.学生成绩统计\n");
	printf("5.学生成绩排名\n");
	printf("6.学生的基本信息和选课信息\n");
	printf("-------------------------------------------------------\n\n");
}

/// 
/// 新生入学信息增加,学生信息修改
/// 
void Maintain_Student_Table() 
{
	string Sno, Sname, Ssex, Sage, Sdept, Scholarship;
	while (true)
	{
		char op;
		system("cls");
		printf("请输入需要的操作 q退出:\n");
		printf("1.新生入学信息增加  2.已有学生信息修改  3.学生信息删除\n");
		rewind(stdin);
		scanf_s("%c", &op, 1);
		if (op == 'q' || op == 'Q')
		{
			printf("学生信息维护结束!\n");
			system("pause");
			break;
		}
		else if (op == '1')
		{
			printf("请依次输入学生信息Sno Sname Ssex Sage Sdept Scholarship:\n");
			cin >> Sno>> Sname >> Ssex >> Sage >> Sdept >> Scholarship;
			string sql = "insert into Student values('";
			sql += Sno; sql += "','"; sql += Sname; sql += "','"; sql += Ssex; sql += "',"; 
			sql += Sage; sql += ",'"; sql += Sdept; sql += "','"; sql += Scholarship; sql += "')";

			retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
			retcode = SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), sql.length());

			if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
			{
				printf("操作失败!错误信息如下:\n");
				// Execute the SQL statement and return any errors or warnings.  
				SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
				// Get the status records.
				i = 1;
				while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA) 
				{
					//DisplayError(SqlState, NativeError, Msg, MsgLen);
					cout << Msg << endl;
					i++;
				}
				system("pause");
			}
			else
			{
				printf("操作成功!");
				system("pause");
			}
			//释放语句句柄 
			retcode = SQLCloseCursor(hstmt);
			retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
		}
		else if (op == '2')
		{
			printf("请输入需要修改的学生的学号\n");
			rewind(stdin);
			cin >> Sno;
			string sql = "delete from Student where Sno = '";
			sql += Sno; sql += '\'';
			retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
			retcode = SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), sql.length());

			if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
			{
				printf("操作失败! 不存在该位同学!\n");
				system("pause");
				retcode = SQLCloseCursor(hstmt);
				retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
			}
			
			else
			{
				printf("请依次输入修改后的学生信息Sno Sname Ssex Sage Sdept Scholarship:\n");
				rewind(stdin);
				cin >> Sno >> Sname >> Ssex >> Sage >> Sdept >> Scholarship;
				string sql = "insert into Student values('";
				sql += Sno; sql += "','"; sql += Sname; sql += "','"; sql += Ssex; sql += "',";
				sql += Sage; sql += ",'"; sql += Sdept; sql += "','"; sql += Scholarship; sql += "')";

				retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
				retcode = SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), sql.length());

				if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
				{
					printf("操作失败!错误信息如下:\n");
					// Execute the SQL statement and return any errors or warnings.  
					SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
					// Get the status records.
					i = 1;
					while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
					{
						//DisplayError(SqlState, NativeError, Msg, MsgLen);
						cout << Msg << endl;
						i++;
					}
					system("pause");
				}
				else
				{
					printf("操作成功!");
					system("pause");
				}
				//释放语句句柄 
				retcode = SQLCloseCursor(hstmt);
				retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
			}

		}
		else if (op == '3')
		{
			printf("请输入需要删除的学生的学号\n");
			cin >> Sno;
			string sql = "delete from Student where Sno = '";
			sql += Sno; sql += '\'';
			retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
			retcode = SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), sql.length());

			if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
			{
				printf("操作失败!错误信息如下:\n");
				// Execute the SQL statement and return any errors or warnings.  
				SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
				// Get the status records.
				i = 1;
				while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
				{
					//DisplayError(SqlState, NativeError, Msg, MsgLen);
					cout << Msg << endl;
					i++;
				}
				system("pause");
			}
			else
			{
				printf("操作成功!");
				system("pause");
			}
			//释放语句句柄 
			retcode = SQLCloseCursor(hstmt);
			retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
		}
		else
		{
			printf("输入错误!请重新输入!\n");
			system("pause");
		}
	}
}

/// 
/// 课程信息维护(增加新课程,修改课程信息,删除没有选课的课程信息)
/// 
void Maintain_Course_Table()
{
	string Cno, Cname, Cpno, Ccredit;
	while (true)
	{
		char op;
		system("cls");
		printf("请输入需要的操作 q退出\n");
		printf("1.增加新课程  2.修改课程信息 3.删除课程信息\n");
		rewind(stdin);
		scanf_s("%c", &op, 1);
		if (op == 'q' || op == 'Q')
		{
			printf("课程信息维护结束!\n");
			system("pause");
			break;
		}
		else if (op == '1')
		{
			printf("请依次输入课程号Cno, 课程名称Cname, 先修课Cpno, 学分Ccredit:\n");
			rewind(stdin);
			cin >> Cno >> Cname >> Ccredit >> Cpno;
			string sql = "insert into Course values('";
			sql += Cno; sql += "','"; sql += Cname; sql += "','"; sql += Cpno; sql += "',"; sql += Ccredit; sql += ')';
			
			retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
			retcode = SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), sql.length());

			if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
			{
				printf("操作失败!错误信息如下:\n");
				// Execute the SQL statement and return any errors or warnings.  
				SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
				// Get the status records.
				i = 1;
				while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
				{
					//DisplayError(SqlState, NativeError, Msg, MsgLen);
					cout << Msg << endl;
					i++;
				}
				system("pause");
			}

			else
			{
				printf("操作成功!");
				system("pause");
			}
			//释放语句句柄 
			retcode = SQLCloseCursor(hstmt);
			retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
		}

		else if (op == '2')
		{
			printf("请输入需要修改的课程号\n");
			rewind(stdin);
			cin >> Cno;
			string sql = "delete from Course where Cno = '";
			sql += Cno; sql += '\'';
			retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
			retcode = SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), sql.length());

			if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
			{
				printf("操作失败! 不存在该课程!\n");
				system("pause");
				retcode = SQLCloseCursor(hstmt);
				retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
			}

			else
			{
				printf("请依次输入修改后的课程信息 课程号Cno, 课程名称Cname, 先修课Cpno, 学分Ccredit:\n");
				rewind(stdin);
				cin >> Cno >> Cname >> Ccredit >> Cpno;
				string sql = "insert into Course values('";
				sql += Cno; sql += "','"; sql += Cname; sql += "','"; sql += Cpno; sql += "',"; sql += Ccredit; sql += ')';

				retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
				retcode = SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), sql.length());

				if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
				{
					printf("操作失败!错误信息如下:\n");
					// Execute the SQL statement and return any errors or warnings.  
					SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
					// Get the status records.
					i = 1;
					while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
					{
						//DisplayError(SqlState, NativeError, Msg, MsgLen);
						cout << Msg << endl;
						i++;
					}
					system("pause");
				}
				else
				{
					printf("操作成功!");
					system("pause");
				}
				//释放语句句柄 
				retcode = SQLCloseCursor(hstmt);
				retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
			}
		}

		else if (op == '3') //自动删除所有没有人选的课程
		{
			string sql = "delete from Course where Cno not in (select Cno from SC)";
			retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
			retcode = SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), sql.length());

			if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
			{
				printf("操作失败!错误信息如下:\n");
				SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
				i = 1;
				while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
				{
					cout << Msg << endl;
					i++;
				}
				system("pause");
			}
			else
			{
				printf("操作成功!");
				system("pause");
			}
			//释放语句句柄 
			retcode = SQLCloseCursor(hstmt);
			retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
		}
		else
		{
			printf("输入错误!请重新输入!\n");
			system("pause");
		}
	}
}

/// 
/// 录入学生成绩,修改学生成绩
/// 
void Maintain_SC_Table()
{
	string Sno, Cno, Grade;
	while (true)
	{
		char op;
		system("cls");
		printf("请输入需要的操作 q退出\n");
		printf("1.录入成绩  2.修改成绩\n");
		rewind(stdin);
		scanf_s("%c", &op, 1);
		if (op == 'q' || op == 'Q')
		{
			printf("学生信息维护结束!\n");
			system("pause");
			break;
		}
		else if (op == '1')
		{
			printf("请依次输入学生学号Sno,考试课程号Cno, 成绩Grade:\n");
			rewind(stdin);
			cin >> Sno >> Cno >> Grade;
			string sql = "insert into SC values('";
			sql += Sno; sql += "','"; sql += Cno; sql += "',"; sql += Grade; sql += ')';
			//cout << sql << endl;
			retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
			retcode = SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), sql.length());

			if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
			{
				printf("操作失败!\n");
				system("pause");
			}
			else
			{
				printf("操作成功!");
				system("pause");
			}
			//释放语句句柄 
			retcode = SQLCloseCursor(hstmt);
			retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
		}
		else if (op == '2')
		{
			printf("请依次输入需要修改的学生学号Sno,考试课程号Cno, 成绩Grade:\n");
			rewind(stdin);
			cin >> Sno >> Cno >> Grade;

			string sql = "update SC set Grade = ";
			sql += Grade; sql += " where Sno = '"; sql += Sno; sql += "' and Cno = '"; sql += Cno; sql += '\'';

			retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
			retcode = SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), sql.length());

			if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
			{
				printf("操作失败!\n");
				system("pause");
			}
			else
			{
				printf("操作成功!");
				system("pause");
			}
			//释放语句句柄 
			retcode = SQLCloseCursor(hstmt);
			retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
		}
		else
		{
			printf("输入错误!请重新输入!\n");
			system("pause");
		}
	}
}


bool cmp(const pair<string, double>& a, const pair<string, double>& b) {
	return a.second > b.second;
}

/// 
/// 按系统计学生的平均成绩、最好成绩、最差成绩、优秀率、不及格人数
/// 
void Grade_Statistic_By_Sdept()
{
	string sql;
	int count = 0;
	int totalnum[100], excellent[100], low[100];   //总人数,优秀人数,不及格人数
	double excellent_rate[100];
	SQLINTEGER num = 0; int i = 0; SQLSMALLINT x = 0;

	//先返回各个系的考试总人数
	i = 0;
	sql = "select Sdept, count(*) from Student, SC where Student.Sno = SC.Sno group by Sdept";
	retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
	retcode = SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), sql.length());
	if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
	{
		printf("操作失败!错误信息如下:\n");
		SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
		i = 1;
		while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
		{
			cout << Msg << endl;
			i++;
		}
		system("pause");
	}
	else
	{
		SQLBindCol(hstmt, 2, SQL_SMALLINT, &x, 0, &num);
		while (TRUE)
		{
			retcode = SQLFetch(hstmt);
			if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
			{
				//show_error();
				printf("错误!\n");
				// Execute the SQL statement and return any errors or warnings.  
				SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
				// Get the status records.
				i = 1;
				while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
				{
					//DisplayError(SqlState, NativeError, Msg, MsgLen);
					cout << SqlState << Msg << endl;
					i++;
				}
				system("pause");
			}
			else if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
				totalnum[i++] = x;
			else
				break;
		}
	}
	retcode = SQLCloseCursor(hstmt);
	retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
	count = i;

	//再返回各个系的优秀人数
	i = 0;
	sql = "select Sdept, count(*) from Student, SC where SC.Sno = Student.Sno and Grade > 90 group by Sdept";
	retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
	retcode = SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), sql.length());
	if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
	{
		printf("操作失败!错误信息如下:\n");
		SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
		i = 1;
		while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
		{
			cout << Msg << endl;
			i++;
		}
		system("pause");
	}
	else
	{
		SQLBindCol(hstmt, 2, SQL_SMALLINT, &x, 0, &num);
		while (TRUE)
		{
			retcode = SQLFetch(hstmt);
			if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
			{
				printf("错误!\n");
				SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
				i = 1;
				while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
				{
					cout << SqlState << Msg << endl;
					i++;
				}
				system("pause");
			}
			else if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
				excellent[i++] = x;
			else
				break;
		}
	}
	retcode = SQLCloseCursor(hstmt);
	retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);

	//再返回各个系的不及格人数
	i = 0;
	sql = "select Sdept, count(*) from Student, SC where SC.Sno = Student.Sno and Grade < 60 group by Sdept";
	retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
	retcode = SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), sql.length());
	if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
	{
		printf("操作失败!错误信息如下:\n");
		SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
		i = 1;
		while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
		{
			cout << Msg << endl;
			i++;
		}
		system("pause");
	}
	else
	{
		SQLBindCol(hstmt, 2, SQL_SMALLINT, &x, 0, &num);
		while (TRUE)
		{
			retcode = SQLFetch(hstmt);
			if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
			{
				printf("错误!\n");
				SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
				i = 1;
				while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
				{
					cout << SqlState << Msg << endl;
					i++;
				}
				system("pause");
			}
			else if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
				low[i++] = x;
			else
				break;
		}
	}
	retcode = SQLCloseCursor(hstmt);
	retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);

	i = 0;
	for (int i = 0; i < count; i++)
	{
		excellent_rate[i] = double(excellent[i]) / totalnum[i];
		//low_rate[i] = double(low[i]) / totalnum[i];
		//printf("%d%% %d%%\n", int(excellent_rate[i]*100), int(low_rate[i]*100));
	}

	SQLSMALLINT max_grade, avg_grade, min_grade;
	SQLINTEGER max_l, avg_l, min_l;

	//最后输出统计信息
	sql = "select Sdept, Avg(Grade) avg_grade, MAX(Grade) max_grade, MIN(Grade) min_grade from Student, SC where SC.Sno = Student.Sno group by Sdept";
	retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
	retcode = SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), sql.length());
	if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
	{
		printf("操作失败!错误信息如下:\n");
		SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
		i = 1;
		while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
		{
			cout << Msg << endl;
			i++;
		}
		system("pause");
	}
	else
	{
		//位数一定要比数据库里定义的至少多一位
		SQLBindCol(hstmt, 1, SQL_C_CHAR, szSdept, 25, &cbSdept);
		SQLBindCol(hstmt, 2, SQL_SMALLINT, &avg_grade, 0, &avg_l);
		SQLBindCol(hstmt, 3, SQL_SMALLINT, &max_grade, 0, &max_l);
		SQLBindCol(hstmt, 4, SQL_SMALLINT, &min_grade, 0, &min_l);
		i = 0;

		printf("%s %s %s %s %s %s\n","院系","平均分","最高分","最低分","优秀率","不及格人数");

		while (TRUE)
		{
			retcode = SQLFetch(hstmt);
			if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
			{
				printf("错误!\n");
				SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
				i = 1;
				while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
				{
					cout << SqlState << Msg << endl;
					i++;
				}
				system("pause");
			}
			else if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
			{
				szSdept[5] = '\0';
				printf("%s %d     %d     %d     %d%%       %d\n", szSdept, avg_grade, max_grade, min_grade, int(excellent_rate[i] * 100), low[i]);
				i++;
			}
			else
				break;
		}
	}

	retcode = SQLCloseCursor(hstmt);
	retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
	system("pause");
}

/// 
/// 按系对学生成绩进行排名,同时显示出学生、课程和成绩信息
/// 
void Grade_Rank_By_Sdept()
{
	map<string, vector<Stu> > m;

	string sql = "select Sdept,Student.Sno,Grade,Ccredit from Student, Course, SC where Student.Sno = SC.Sno and SC.Cno = Course.Cno";
	retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
	retcode = SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), sql.length());

	if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
	{
		printf("操作失败!错误信息如下:\n");
		SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
		i = 1;
		while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
		{
			cout << Msg << endl;
			i++;
		}
		system("pause");
	}
	else
	{
		SQLBindCol(hstmt, 1, SQL_C_CHAR, szSdept, 25, &cbSdept);
		SQLBindCol(hstmt, 2, SQL_C_CHAR, szSno, 10, &cbSno);
		SQLBindCol(hstmt, 3, SQL_SMALLINT, &szGrade, 0, &cbGrade);
		SQLBindCol(hstmt, 4, SQL_SMALLINT, &szCcredit, 0, &cbCcredit);

		while (TRUE)
		{
			string sno,sdept;
			retcode = SQLFetch(hstmt);
			if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
			{
				//show_error();
				printf("错误!\n");
				// Execute the SQL statement and return any errors or warnings.  
				SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
				// Get the status records.
				i = 1;
				while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
				{
					//DisplayError(SqlState, NativeError, Msg, MsgLen);
					cout << SqlState << Msg << endl;
					i++;
				}
				system("pause");
			}
			else if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
			{
				//zSdept[5] = '\0';
				for (int i = 0; i < 3; i++)
					sdept.push_back(szSdept[i]);
				for (int i = 0; i < 10; i++)
					sno.push_back(szSno[i]);
				m[sdept].push_back(Stu(szGrade, szCcredit, sno));
			}
			else
			{
				break;
			}
		}

		//按系统计
		map<string, double> stu_grade;  //加权成绩
		for (auto it = m.begin(); it!= m.end(); it++)
		{
			system("cls");
			stu_grade.clear();
			int stu_num = it->second.size();
			
			int sum = 0, i;
			int class_count = 0;  //一共多少学分
			for (i = 0; i < stu_num; i++)
			{
				if (stu_grade.find(it->second[i].sno) == stu_grade.end() && i!=0)  //出现了新同学,计算之前的,并重新初始化
				{
					stu_grade[it->second[i - 1].sno] = double(sum) / class_count;
					sum = 0;
					class_count = 0;
				}
				class_count += it->second[i].credit;
				sum += it->second[i].credit * it->second[i].grade;
			}
			stu_grade[it->second[i-1].sno] = double(sum) / class_count;  //最后一个同学单独计算

			vector<pair<string, double>> vec(stu_grade.begin(), stu_grade.end());
			//对线性的vector进行排序
			sort(vec.begin(), vec.end(), cmp);
			
			printf("开始展示:%s系学生排名\n", it->first.c_str());
			
			for (int i = 0; i < vec.size(); ++i)
			{
				printf("排名      学号     加权成绩\n");
				printf("Rank%d  %s    %.2f\n", i + 1, vec[i].first.c_str(), vec[i].second);
				printf("成绩如下:\n");
				string sql = "select Sname, Cname, Grade from Student, SC, Course where Student.Sno = '";
				sql += vec[i].first;
				sql += "' and SC.Sno = Student.Sno and Course.Cno = SC.Cno";

				retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
				retcode = SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), sql.length());

				if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
				{
					printf("操作失败!错误信息如下:\n");
					SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
					i = 1;
					while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
					{
						cout << Msg << endl;
						i++;
					}
					system("pause");
				}
				else
				{
					SQLBindCol(hstmt, 1, SQL_C_CHAR, szSname, 25, &cbSname);
					SQLBindCol(hstmt, 2, SQL_C_CHAR, szCname, 45, &cbCname);
					SQLBindCol(hstmt, 3, SQL_SMALLINT, &szGrade, 0, &cbGrade);
					while (TRUE)
					{
						retcode = SQLFetch(hstmt);
						if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
						{
							//show_error();
							printf("错误!\n");
							// Execute the SQL statement and return any errors or warnings.  
							SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
							// Get the status records.
							i = 1;
							while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
							{
								//DisplayError(SqlState, NativeError, Msg, MsgLen);
								cout << SqlState << Msg << endl;
								i++;
							}
							system("pause");
						}
						else if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
						{
							szSname[8] = '\0'; szCname[15] = '\0';
							printf("%s   %s  %d\n",szSname, szCname, szGrade);
						}
						else
						{
							break;
						}
					}
					printf("\n");
				}

				//释放语句句柄 
				retcode = SQLCloseCursor(hstmt);
				retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
			}
			printf("\n");
			system("pause");
		}
		//system("pause");
	}

	//释放语句句柄 
	retcode = SQLCloseCursor(hstmt);
	retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
}

/// 
/// 输入学号,显示该学生的基本信息和选课信息
/// 
void Get_Stu_Info()
{
	string sname, sdept, cname;
	string Sno;
	while (true)
	{
		system("cls");
		printf("请输入需要查询的学生学号Sno 按q退出\n");
		rewind(stdin);
		cin >> Sno;
		if (Sno == "q" || Sno == "Q")
		{
			printf("查询结束!\n");
			system("pause");
			break;
		}
		else
		{
			string sql = "select Student.Sno,Sname,Ssex,Sage,Sdept,Scholarship,SC.Cno,Cname,Grade from Student, Course, SC where Student.Sno = '";
			sql += Sno; sql += "' and Student.Sno = SC.Sno and SC.Cno = Course.Cno";

			retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
			retcode = SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), sql.length());

			if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
			{
				printf("操作失败!错误信息如下:\n");
				SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
				i = 1;
				while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
				{
					cout << Msg << endl;
					i++;
				}
				system("pause");
			}
			else
			{
				//位数一定要比数据库里定义的至少多一位
				SQLBindCol(hstmt, 1, SQL_C_CHAR, szSno, 10, &cbSno);
				SQLBindCol(hstmt, 2, SQL_C_CHAR, szSname, 25, &cbSname);
				SQLBindCol(hstmt, 3, SQL_C_CHAR, szSsex, 3, &cbSsex);
				SQLBindCol(hstmt, 4, SQL_SMALLINT, &szSage, 0, &cbSage);
				SQLBindCol(hstmt, 5, SQL_C_CHAR, szSdept, 25, &cbSdept);
				SQLBindCol(hstmt, 6, SQL_C_CHAR, szScholarship, 5, &cbScholarship);
				SQLBindCol(hstmt, 7, SQL_C_CHAR, szCno, 5, &cbCno);
				SQLBindCol(hstmt, 8, SQL_C_CHAR, szCname, 45, &cbCname);
				SQLBindCol(hstmt, 9, SQL_SMALLINT, &szGrade, 0, &cbGrade);

				printf("%s         %s   %3s    %s   %s    %s   %s   %s        %s\n", "Sno", "Sname", "Ssex", "Sage", "Sdept", "Scholarship", "Cno", "Cname", "Grade");
				while (TRUE)
				{
					retcode = SQLFetch(hstmt);
					if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
					{
						//show_error();
						printf("错误!\n");
						// Execute the SQL statement and return any errors or warnings.  
						SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
						// Get the status records.
						i = 1;
						while (i <= numRecs && (rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
						{
							//DisplayError(SqlState, NativeError, Msg, MsgLen);
							cout << SqlState << Msg << endl;
							i++;
						}
						system("pause");
					}
					else if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
					{
						szSname[8] = '\0'; szSdept[5] = '\0'; szCname[15] = '\0';
						printf("%-10s  %-5s %-3s     %d     %-5s       %-3s       %-2s  %-10s%d\n", szSno, szSname, szSsex, szSage, szSdept, szScholarship, szCno, szCname, szGrade);
					}
					else
					{
						break;
					}
				}
				system("pause");
			}
			//释放语句句柄 
			retcode = SQLCloseCursor(hstmt);
			retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
		}
	}
}

int main()
{
	SQLCHAR szDSN[15] = "mysql"; //数据源名称
	SQLCHAR userID[6] = "sa";//数据库用户ID
	SQLCHAR passWORD[29] = "";//用户密码
	//1.连接数据源  
	//1.环境句柄   
	//对于任何ODBC应用程序来说,第一步的工作是装载驱动程序管理器,然后初始化ODBC环境,分配环境句柄。
    //首先,程序中声明一个SQLHENV类型的变量,然后调用函数SQLAllocHandle,向其中传递分配的上述SQLHENV类型的变量地址和SQL_HANDLE_ENV选项。
	retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv); //执行该调用语句后,驱动程序分配一个结构,该结构中存放环境信息,然后返回对应于该环境的环境句柄。
	retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
	//2.连接句柄  
	//分配环境句柄后,在建立至数据源的连接之前,我们必须分配一个连接句柄,每一个到数据源的连接对应于一个连接句柄。
    //首先,程序定义了一个SQLHDBC类型的变量,用于存放连接句柄,然后调用SQLAllocHandle函数分配句柄。
	retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc); 
	retcode = SQLConnect(hdbc, szDSN, SQL_NTS, userID, SQL_NTS, passWORD, SQL_NTS);
	/*
	SQLConnect
    该函数提供了最为直接的程序控制方式,我们只要提供数据源名称、用户ID和口令,就可以进行连接了。
    函数格式:
    SQLRETURN SQLConnect(SQLHDBC ConnectionHandle,SQLCHAR ServerName,SQLSMALLINT NameLength1,SQLCHAR UserName,SQLSMALLINT NameLength2,SQLCHAR *Authentication,SQLSMALLINT NameLength3);
    参数:
    ConnectionHandle 连接句柄
    ServerName 数据源名称
    NameLength1 数据源名称长度
    UserName 用户ID
    NameLength2 用户ID长度
    Authentication 用户口令
    NameLength3 用户口令长度
    返回值:
    SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.
    成功返回SQL_SUCCESS,如果返回值为SQL_ERROR或SQL_SUCCESS_WITH_INFO,可以用函数SQLGetDiagRec获取相应SQLSTATE的值。 
	*/
	//判断连接是否成功   
	if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
	{
		printf("连接到数据库失败!\n");
	}
	else
	{
		char op;
		while (true)
		{
			system("cls");
			showmenu();
			printf("请输入需要执行的操作,按q退出!\n");
			rewind(stdin);
			scanf_s("%c", &op, 1);
			if (op == 'q' || op == 'Q')
			{
				printf("再见,欢迎下次使用!\n");
				break;
			}
			else
			{
				switch (op)
				{
				case '1': Maintain_Student_Table();  break;
				case '2': Maintain_Course_Table();;  break;
				case '3': Maintain_SC_Table();  break;
				case '4': Grade_Statistic_By_Sdept();  break;
				case '5': Grade_Rank_By_Sdept();  break;
				case '6': Get_Stu_Info();  break;
				default: printf("输入错误,请重新输入!\n"); break;
				}
			}
		}
	}
	//3.断开数据库连接
	/*
	1. 断开数据库连接
	2.释放连接句柄.
	3.释放环境句柄(如果不再需要在这个环境中作更多连接)
	*/
	SQLDisconnect(hdbc);
	SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
	SQLFreeHandle(SQL_HANDLE_ENV, henv);
	return(0);
}

对于你自己而言,不同的数据库,只需要修改main函数里的数据源名称(ODBC源配置里设置的),数据库用户ID(一般就是sa)和用户密码(自己设置的sa密码),在你也建立好之前的三个表并插入数据后,该程序可以直接运行。

你可能感兴趣的:(操作数据库,数据库,sqlserver,c++)