使用c语言对sql server进行增删改查操作

使用c语言对sql server进行增删改查操作

众所周知,自从sql server 2000以后,便不再支持c语言的嵌入式语言操作,因此需要使用odbc连接的方式来进行操作。我在完成实验的时候一直找不到较为完整的操作代码,走了不少弯路,在这里分享给大家我的一些总结。

环境
windows 10
sql server 2019
vs2019
注意:请使用多字节字符集

  1. ODBC连接
    这一部分我就不做详细的赘述,在论坛中很容易搜索的到。
  2. 连接数据库
#include<stdio.h>
#include<windows.h>
#include<sql.h>
#include<sqlext.h>
#include<sqltypes.h>

void Connect(SQLRETURN ret,
   SQLHENV& henv,
   SQLHDBC& hdbc,
   SQLHSTMT& hstmt)//设置连接参数的句柄

{
   const char* SY1 = "DOST";
   unsigned char* SY = (unsigned char*)SY1;
   const char* db21 = "sa";//数据库ODBC连接的账户
   unsigned char* db2 = (unsigned char*)db21;
   const char* pass1 = "12345678";//数据库ODBC连接的密码
   unsigned char* pass = (unsigned char*)pass1;

   ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);//申请环境句柄
   ret = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, SQL_IS_INTEGER);
   ret = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);//申请数据库连接句柄

   ret = SQLConnect(hdbc, SY, SQL_NTS, db2, SQL_NTS, pass, SQL_NTS);
   /*db2为配置的ODBC数据源名称,这里根据自己的配置进行修改*/
   if (!(ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO))
   {
   	printf("连接数据库失败!\n");
   	return;
   }

   ret = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);//用于分配句柄
}

3.增加功能

	void insert(SQLRETURN ret,
	SQLHENV& henv,
	SQLHDBC& hdbc,
	SQLHSTMT& hstmt)
{
	int i = 0,j = 0,k = 0;
	unsigned char tablename[60];
	do
	{
		printf("请输入你想要插入数据的表的序号\n");
		printf("1.学生表S 2.课程表C 3.学生成绩表SC\n");
		scanf("%d", &i);
		switch (i)
		{
		case 1:strcpy((char*)tablename, "S(sclass,sno,sname,ssex,Sdept,sage) values (?,?,?,?,?,?)");printf("请根据S(sclass,sno,sname,ssex,Sdept,sage)依次输入数据,数据之间带回车或空格\n"); break;//非常明显这里采用了动态sql的方式,问号的地方即为我们后期需要输入的地方
		case 2:strcpy((char*)tablename, "C values (?,?,?,?)"); printf("请根据C(cno,cname,cpno,ccredit)依次输入数据,数据之间带回车或空格\n"); break;
		case 3:strcpy((char*)tablename, "SC values (?,?,?,?)"); printf("请根据SC(sclass,sno,cno,grade)依次输入数据,数据之间带回车或空格\n"); break;
		default:printf("输入错误,重新输入\n");
		}
	} while (i < 1 || i>3);
	char s[5][10], s1[10] = "", s2[10] = "", s3[10] = "", s4[10] = "", s5[10] = "";
	int num;
	/*long conlumnlen;*/
	SQLAllocStmt(hdbc, &hstmt);
	SQLCHAR sql2[100] = "insert into ";//要注意,sqlchar实际上就是unsigned char,这在字符串操作语句当中是不能够直接操作的,需要强转为char型后再进行操作
	strcat((char*)sql2, (char*)tablename);
	//printf("%s\n", sql2);
	SQLPrepare(hstmt, (SQLCHAR*)sql2, strlen((char*)sql2));//这里是对sql语句进行prepare操作,这一步有助于防注入,提高安全性
	if (i == 1)
		j = 5;
	else if (i == 2|| i==3)
		j = 3;
	for (k = 0; k < j; k++)
	{
		scanf("%s", s[k]);
		SQLBindParameter(hstmt, k+1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, &s[k], 100, NULL);//sqlbindparameter函数操作用于绑定刚才的问号的位置,注意,上方的“k+1”的数值即为问号在sql语句中的位置,顺序必须一致,而且如果sql在绑定后再修改,这里的绑定将会出错。
	}
	scanf("%d", &num);
	SQLBindParameter(hstmt, k+1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 8, 0, &num, sizeof(int), NULL);
	
	SQLExecute(hstmt);
	
	if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
	{
		printf("操作成功!\n");
	}
	else
	{
		UCHAR errmsg[100];
		printf("操作失败!\n");
		SQLError(henv, hdbc, hstmt, NULL, NULL, errmsg, sizeof(errmsg), NULL);
		printf("%s", errmsg);
	}
	
}

4.删除功能

void deleteD(SQLRETURN ret,
	SQLHENV& henv,
	SQLHDBC& hdbc,
	SQLHSTMT& hstmt)
{
	int i = 0;
	unsigned char tablename[60];
	do
	{
		printf("请输入你想要删除数据的表的序号\n");
		printf("1.学生表S 2.课程表C 3.学生成绩表SC\n");
		scanf("%d", &i);
		switch (i)
		{
		case 1:strcpy((char*)tablename, "S where sclass = ? and sno= ? "); printf("请输入班号和学号,之间用空格隔开\n"); break;
		case 2:strcpy((char*)tablename, "C where cno= ? "); printf("请输入课程号\n"); break;
		case 3:strcpy((char*)tablename, "SC where sclass =? and sno=? and cno=? "); printf("请输入班号学号和课程号,之间用空格隔开\n"); break;
		default:printf("输入错误,重新输入\n");
		}
	} while (i < 1 || i>3);
	unsigned char s[3][10];
	SQLCHAR sql[120] = "delete from ";
	/*long conlumnlen;*/
	SQLAllocStmt(hdbc, &hstmt);
	strcat((char*)sql, (char*)tablename);
	SQLPrepare(hstmt, (SQLCHAR*)sql, strlen((char*)sql));
	if (i == 2)
	{
		scanf("%s", s[0]);
		SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, &s[0], 100, NULL);
	}
	else if (i == 1)
	{
		scanf("%s", s[0]);
		scanf("%s", s[1]);
		SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, &s[0], 100, NULL);
		SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, &s[1], 100, NULL);
	}
	else if (i == 3)
	{
		scanf("%s", s[0]);
		scanf("%s", s[1]);
		scanf("%s", s[2]);
		SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, &s[0], 100, NULL);
		SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, &s[1], 100, NULL);
		SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, &s[2], 100, NULL);
	}
	SQLExecute(hstmt);//sql执行
	if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
	{
		printf("操作成功!\n");
	}
	else
	{
		UCHAR errmsg[100];
		printf("操作失败!\n");
		SQLError(henv, hdbc, hstmt, NULL, NULL, errmsg, sizeof(errmsg), NULL);
		printf("%s", errmsg);
	}
}

很明显删除操作与刚才的增加操作有非常多相似的地方,只不过是在sql语句上有所不同,无论是sql语句连接,sqlprepare还是sqlexecute顺序基本一致,同样采用了动态sql的方式。

5.修改功能

void edit(SQLRETURN ret,
	SQLHENV& henv,
	SQLHDBC& hdbc,
	SQLHSTMT& hstmt)
{
	int i = 0;
	unsigned char tablename1[60],tablename2[60];
	unsigned char s[3][10];
	do
	{
		printf("请输入你想要修改数据的表的序号\n");
		printf("1.学生表S 2.课程表C 3.学生成绩表SC\n");
		scanf("%d", &i);
		switch (i)
		{
		case 1:strcpy((char*)tablename1, "S set "); strcpy((char*)tablename2, "where sclass=? and sno=?");break;
		case 2:strcpy((char*)tablename1, "C set "); strcpy((char*)tablename2, "where cno=? ");break;
		case 3:strcpy((char*)tablename1, "SC set "); strcpy((char*)tablename2, "where sclass=? and sno=? and cno=?"); break;
		default:printf("输入错误,重新输入\n");
		}
	} while (i < 1 || i>3);
	unsigned char s1[10];
	char part[14][10];
	strcpy(part[0], "sage=? ");
	strcpy(part[1], "sclass=? ");
	strcpy(part[2], "sno=? ");
	strcpy(part[3], "sname=? ");
	strcpy(part[4], "ssex=? ");
	strcpy(part[5], "Sdept=? ");
	strcpy(part[6], "cno=? ");
	strcpy(part[7], "cname=? ");
	strcpy(part[8], "cpno=? ");
	strcpy(part[9], "ccredit=? ");
	strcpy(part[10], "grade=? ");
	
	int j = 0,num;
	do {
		printf("选择修改的元素名\n");
		printf("1.sage 2.sclass 3.sno 4.sname 5.ssex 6.Sdept 7.cno 8.cname 9.cpno 10.ccredit 11.grade\n");
		scanf("%d", &j);
	} while (j < 1 || j>11);
	SQLCHAR sql[100] = "update ";
	/*long conlumnlen;*/
	SQLAllocStmt(hdbc, &hstmt);
	strcat((char*)sql, (char*)tablename1);
	strcat((char*)sql, part[j-1]);
	strcat((char*)sql, (char*)tablename2);
	printf("%s\n", sql);
	SQLPrepare(hstmt, (SQLCHAR*)sql, strlen((char*)sql));
	if (i == 2)
	{
		printf("请输入课程号:");
		scanf("%s", s[0]);
		SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, &s[0], 100, NULL);
	}
	else if (i == 1)
	{
		printf("请输入班别学号:");
		scanf("%s", s[0]);
		scanf("%s", s[1]);
		SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, &s[0], 100, NULL);
		SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, &s[1], 100, NULL);
	}
	else if (i == 3)
	{
		printf("请输入班别学号和课程号:");
		scanf("%s", s[0]);
		scanf("%s", s[1]);
		scanf("%s", s[2]);
		SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, &s[0], 100, NULL);
		SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, &s[1], 100, NULL);
		SQLBindParameter(hstmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, &s[2], 100, NULL);
	}
	printf("输入修改元素值:");
	if (j == 1||j==11)
	{
		scanf("%d", &num);
		SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 8, 0, &num, sizeof(int), NULL);
		
	}
	else
	{
		scanf("%s", s1);
		SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, &s, 100, NULL);
	}
	ret= SQLExecute(hstmt);
	if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
	{
		printf("操作成功!\n");
	}
	else
	{
		UCHAR errmsg[100];
		printf("操作失败!\n");
		SQLError(henv, hdbc, hstmt, NULL, NULL, errmsg, sizeof(errmsg), NULL);
		printf("%s", errmsg);
	}
}

这里同样与上方采用的方法是一致的。
6.查询功能

查询功能分为精确查询和模糊查询,精确查询可以采用动态sql的方式,但是由于动态sql的?传进去后将被直接识别为一个元素,因此模糊查询只能用字符串连接的方式来完成。

void Search(SQLRETURN ret,
	SQLHENV& henv,
	SQLHDBC& hdbc,
	SQLHSTMT& hstmt)
{
	SQLAllocStmt(hdbc, &hstmt);
	unsigned char sno[10], sclass[10];
	printf("请输入班级 学号:(之间用空格分隔)");
	scanf("%s %s", sclass, sno);
	int i;
	printf("请输入你想查询的功能:1.查询你的学生信息 2.查询你的所有成绩\n");
	scanf("%d", &i);
	SQLCHAR sql[150];
	if(i==1)
		strcpy((char*)sql,"select * from S where sclass= ? and sno= ? ");
	else if(i==2)
		strcpy((char*)sql, "select a.sname,b.*,c.cname from S a, SC b, C c where b.sclass = ? and b.sno= ? and a.sno=b.sno and a.sclass=b.sclass and c.cno=b.cno");
	SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, &sclass, 100, NULL);
	SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 0, &sno, 100, NULL);
	char S[6][10];
	long conlumnlen;
	int snum=0;
	if (i == 1)
	{
		SQLPrepare(hstmt, (SQLCHAR*)sql, strlen((char*)sql));
		SQLBindCol(hstmt, 1, SQL_CHAR, S[0], 10, &conlumnlen);
		SQLBindCol(hstmt, 2, SQL_CHAR, S[1], 10, &conlumnlen);
		SQLBindCol(hstmt, 3, SQL_CHAR, S[2], 10, &conlumnlen);
		SQLBindCol(hstmt, 4, SQL_CHAR, S[3], 10, &conlumnlen);
		SQLBindCol(hstmt, 6, SQL_CHAR, S[4], 10, &conlumnlen);
		SQLBindCol(hstmt, 5, SQL_INTEGER, &snum, sizeof(int), &conlumnlen);
		printf("sclass\tsno\tsname\tssex\tSdept\tsage\n");
		SQLExecute(hstmt);
	}
	else if(i==2)
	{
		SQLPrepare(hstmt, (SQLCHAR*)sql, strlen((char*)sql));
		SQLBindCol(hstmt, 1, SQL_CHAR, S[0], 10, &conlumnlen);
		SQLBindCol(hstmt, 2, SQL_CHAR, S[1], 10, &conlumnlen);
		SQLBindCol(hstmt, 3, SQL_CHAR, S[2], 10, &conlumnlen);
		SQLBindCol(hstmt, 4, SQL_CHAR, S[3], 10, &conlumnlen);
		SQLBindCol(hstmt, 6, SQL_CHAR, S[4], 10, &conlumnlen);
		SQLBindCol(hstmt, 5, SQL_INTEGER, &snum, sizeof(int), &conlumnlen);
		printf("sname\tsclass\tsno\tcname\tcno\tgrade\n");
		SQLExecute(hstmt);
	}
	ret = SQLFetch(hstmt);
	if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
	{
		printf("操作成功!\n");
		while (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
		{
			if (i == 1)
				printf("%s%s%s%s%s%d\n", S[0], S[1], S[2], S[3], S[4], snum);
			else
				printf("%s%s%s%s%s%d\n", S[0], S[1], S[2], S[4], S[3], snum);
			ret = SQLFetch(hstmt);
		}
	}
	else
	{
		UCHAR errmsg[100];
		printf("操作失败!\n");
		SQLError(henv, hdbc, hstmt, NULL, NULL, errmsg, sizeof(errmsg), NULL);
		printf("%s", errmsg);
	}
}

上方出现了sqlbindcol函数,是用于查询完后绑定c语言中定义的变量当中,嵌入式sql也是这样的原理,只不过操作更为的直接一些。同样的,绑定的顺序也需要与数据库中你所查询的列相匹配。
着重谈谈模糊查询的连接操作,由于直接采用执行sql语句的方式,模糊查询的模板是下方。

select ··· 
from ··· like %···%

因此我们操作代码为:

char input[100];
	char sno[10] = "", sname[10] = "", ssex[4] = "", sage[4] = "", saddr[20] = "";
	printf("输入地址查询对应数据\n");
	scanf("%s", input); 
	long conlumnlen;
	SQLAllocStmt(hdbc, &hstmt);
	SQLCHAR sql2[100] = "select * from dbo.S1 where SADDR like '%";//注意这里的百分号后面千万不要加上空格,否则你几乎是不可能看出来不同的
	strcat((char*)sql2, input);
	strcat((char*)sql2, "%'");
	SQLPrepare(hstmt, (SQLCHAR*)sql2, strlen((char*)sql2));
	
	/*ret = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 100, 0, &input, 100, NULL);*/
	SQLBindCol(hstmt, 1, SQL_CHAR, sno, 10, &conlumnlen);
	SQLBindCol(hstmt, 2, SQL_CHAR, sname, 10, &conlumnlen);
	SQLBindCol(hstmt, 3, SQL_CHAR, ssex, 4, &conlumnlen);
	SQLBindCol(hstmt, 4, SQL_CHAR, sage, 4, &conlumnlen);
	SQLBindCol(hstmt, 5, SQL_CHAR, saddr, 20, &conlumnlen);
	SQLExecute(hstmt);

	ret = SQLFetch(hstmt);
	if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
	{
		printf("操作成功!\n");
		while (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
		{
			printf("%s\t%s\t%s\t%s\t%s\n", sno, sname, ssex, sage, saddr);
			ret = SQLFetch(hstmt);
		}
	}
	else
	{
		UCHAR errmsg[100];
		printf("操作失败!\n");
		SQLError(henv, hdbc, hstmt, NULL, NULL, errmsg, sizeof(errmsg), NULL);
		printf("%s", errmsg);
	}
	SQLFreeConnect(hdbc);
	SQLFreeEnv(henv);

采用这样的字符串连接方式是存在弊端的,可能会被sql注入攻击。sqlfetch是游标,用作将所有结果输出,相信大家应该都学过了,就不做分析了。

SQLCHAR sql2[100] = "select * from dbo.S1 where SADDR like '%";
//注意这里的百分号后面千万不要加上空格,否则你几乎是不可能看出来不同的
	strcat((char*)sql2, input);
	strcat((char*)sql2, "%'");

7.总结

现在语言这么的丰富,用这种底层函数来连接数据库实在是有点麻烦了,那么多的库函数库函数不用用这个。不过如果是在学校当中的话,在更为底层的方面开始了解对以后的工作和学习都有所好处。

8.感想

非常明显,这篇文章无论从语言还是结构上来说都非常的松散随意,因此如果存在任何的错误,欢迎留言,我会做出修改。
ps:这是我的数据库实验作业,如果后来者有参考的,千万不要是跟我一个学校的,张淼老师会看出来的哦,毕竟没几个懒人连PB都懒得下

  • 6
    点赞
  • 98
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值