学会使用ODBC API
虽说以前也接触过一些数据库的应用,但是由于当时只是抱着非常浅显的数据库知识,做了一些SQL语句上的操作,安装-配置-连接这一系列的流程全是自己照着例子实现的,大概记住的也就是ODBC、ADO之类,也简单的理解了一下,当共享使用数据库时最好使用ODBC的方式,而本地的数据库就可以使用ADO。
具体区别这些连接方式倒是就可以好好学习一下了:
- MDAC:http://zh.wikipedia.org/wiki/MDAC
- ODBC:http://zh.wikipedia.org/zh-cn/ODBC
- ADO:http://zh.wikipedia.org/wiki/ADO
简单的来说,ODBC和ADO都是微软提供的数据库访问方式,ADO是一个COM组件,而ODBC则是一种标准的访问方式,结束了数据库访问和数据库相关的局面,只不过两者实现的不同,所以在不同应用的表象上不同罢了(不对的话请指出,只看了Wiki上的一点介绍,没有系统学习)。
使用
通过分析考虑到将来可能会采用共享数据库的方式,此外对于COM自己也不是很熟悉,所以还是使用ODBC的方式比较好,使用ODBC的方式就很多了,但是由于我并不是程序开发人员,现在负责维护,不大敢做大手术,所以就选择了ODBC API,这就相当于使用WINDOS API和MFC一样,虽然API复杂,但是相对自由很多。
首先要在程序中添加几个依赖项
#include <sqlext.h>
#include <odbcinst.h>
#pragma comment(lib,"odbc32.lib")
#pragma comment(lib,"odbccp32.lib")
前两个头文件分别代表基本的ODBC API和高级的ODBC API,最后一个好像是包含了更高级的API(暂时这么理解吧),一般用到前两个即可,odbc32.lib对应sql.h和sqlext.h,odbccp32.lib对应odbcinst.h。
所有使用数据库的过程无非是这样一个流程:
1. 想办法建立数据库的连接
2. 想办法执行SQL语句,并使用结果
3. 不用时关闭连接,以免下次访问报错
对于像我这样不常用数据库的菜鸟来说,这样的方式足够完成我所面临的问题了,诸如互斥访问、分布式之类的问题还是暂时留给高手们考虑吧。
建立数据库连接
在我们要连接数据库的类中,或者全局变量下建立相关的变量
SQLHDBC m_hDatabaseConnection;//数据库连接句柄,老大以后就是他了,有了他数据库就连接上了
SQLHSTMT m_hStatement;//执行语句句柄,最终执行SQL于句的句柄
使用ODBC API建立数据库连接分为3部分:申请环境句柄,使用环境句柄申请连接句柄、使用连接句柄连接数据库。
SQLRETURN l_uiReturn = SQLAllocHandle(SQL_HANDLE_ENV,NULL,&m_hEnviroment);
//申请各种句柄都靠这个函数,参数1是要申请句柄的类型,参数2为申请该句柄依靠的句柄(老大没依靠,所以是NULL),申请结果在参数3中保存
//返回值代表着执行的意义,如下面判断,SUCCESS_WITH_INFO相当于是警告,虽然成功了,但是可能有问题
if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO)
{
return false;
}
SQLSetEnvAttr(m_hEnviroment,SQL_ATTR_ODBC_VERSION,(SQLPOINTER)SQL_OV_ODBC3,SQL_IS_INTEGER);
l_uiReturn = SQLAllocHandle(SQL_HANDLE_DBC,m_hEnviroment,&m_hDatabaseConnection);
if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO)
{
return false;
}
SQLWCHAR * l_swcaDsnName = _T("");//数据源名称
SQLWCHAR * l_swcaUserName = _T("");//用户名称
SQLWCHAR * l_swcaPassWord = _T("");//密码
l_uiReturn = SQLConnect(m_hDatabaseConnection,l_swcaDsnName,SQL_NTS
,l_swcaUserName,SQL_NTS
,l_swcaPassWord,SQL_NTS);
if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO)
{
return false;
}
使用SQL语句
SQLRETURN l_uiReturn = SQLAllocHandle(SQL_HANDLE_STMT,m_hDatabaseConnection,&m_hStatement);
if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO)
{
return 0;
}
/* 构造SQL语句 */
CString l_cstrSql;
l_cstrSql.Format(_T("SELECT * FROM 数据表 "));
/* 执行SQL语句 */
l_uiReturn = SQLExecDirect(m_hStatement,l_cstrSql.GetBuffer(),SQL_NTS);
if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO)
{
return 0;
}
/* 获得返回结果的行数 */
SQLINTEGER l_siIdCount = 0;
l_uiReturn = SQLRowCount(m_hStatement,&l_siIdCount);
/* 开始读取结果 *///读取第一行时要调用,以后依次调用就可以下移行数,直到不返回SQL_SUCCESS
l_uiReturn = SQLFetch(m_hStatement);
if (l_uiReturn != SQL_SUCCESS && l_uiReturn != SQL_SUCCESS_WITH_INFO)
{
return 0;
}
SQLINTEGER l_siID;
SQLINTEGER l_siIDLength = 0;
/* 获得数据 */
SQLGetData(m_hStatement,1,SQL_C_ULONG,&l_siID,0,&l_siIDLength);
//参数1为执行语句的句柄,参数2为所要得到的数据位于的列数(SQL语句中),参数3为数据类型,这个比较多,需要看一下MSDN
//参数4为保存的位置(地址),参数5为参数4可用的位置,既然参数3已设定为长整型,所以这里可使用0
//参数6为实际返回的长度
/* 释放语句句柄 */
SQLFreeHandle(SQL_HANDLE_STMT,m_hStatement);
断开连接
SQLFreeHandle(SQL_HANDLE_DBC,m_hDatabaseConnection);
SQLFreeHandle(SQL_HANDLE_ENV,m_hEnviroment);
后记:客户需求与实现方式的思考
客户来了新的需求-交换:当时的原型为,他们那里有一套嵌入式的系统负责从传感器上获得数据,他们也有一套相应的管理程序,但是但是那套管理程序缺乏显示效果,所以需要利用我们所做的系统进行显示。
根据这个需求,我们(其实只有我一个人)进行了分析,当时客户提出的最好使用SOCKET变成来实现,按说这也是一个不错的主意,自由且不是很复杂,但是带来的后果就可能是以后如果各种交互多了以后,极难维护。所以觉得使用一个共同的数据库比较好,他们负责生成数据,我们负责读取并显示,这种好处是可以最大的运用程序的效率,不用总是考虑SOCKET能不能及时的接收到,而且责任分明,也方便维护,但是现在考虑这种方式的缺点就是,我们的代码有一定的重复,他们的客户端就有一些根据数据条件进行“分类”的意思,我们这边则要根据数据进行不同的显示效果。
本文链接
在SQL2000里的网络实用工具里有一个选项:隐藏服务器,这样当你枚举局域网中的SQL2000服务器的时候就会搜索不了
以为SQL2005没有了这个功能,今晚发现原来在配置管理器里
大家看下面几张图就晓得步骤了,设置完毕之后需要重启SQL服务哦o(∩_∩)o
SQL2000的
SQL2005的
设置了隐藏实例之后就搜索不了
不隐藏实例
本文链接
SQL 不同于与其他编程语言的最明显特征是处理代码的顺序。在大数编程语言中,代码按编码顺序被处理,但是在SQL语言中,第一个被处理的子句是FROM子句,尽管SELECT语句第一个出现,但是几乎总是最后被处理。
每个步骤都会产生一个虚拟表,该虚拟表被用作下一个步骤的输入。这些虚拟表对调用者(客户端应用程序或者外部查询)不可用。只是最后一步生成的表才会返回 给调用者。如果没有在查询中指定某一子句,将跳过相应的步骤。下面是对应用于SQL server 2000和SQL Server 2005的各个逻辑步骤的简单描述。
(1)FROM [left_table]
(3)<join_type> JOIN <right_table>
(2)ON <join_condition>
(4)WHERE <where_condition>
(5)GROUP BY <group_by_list>
(6)WITH <CUBE | RollUP>
(7)HAVING <having_condition>
(10)ORDER BY <order_by_list>
逻辑查询处理阶段简介
注:步骤10,按ORDER BY子句中的列列表排序上步返回的行,返回游标VC10.这一步是第一步也是唯一一步可以使用SELECT列表中的列别名的步骤。这一步不同于其它步骤的 是,它不返回有效的表,而是返回一个游标。SQL是基于集合理论的。集合不会预先对它的行排序,它只是成员的逻辑集合,成员的顺序无关紧要。对表进行排序 的查询可以返回一个对象,包含按特定物理顺序组织的行。ANSI把这种对象称为游标。理解这一步是正确理解SQL的基础。
因为这一步不返回表(而是返回游标),使用了ORDER BY子句的查询不能用作表表达式。表表达式包括:视图、内联表值函数、子查询、派生表和共用表达式。它的结果必须返回给期望得到物理记录的客户端应用程序。例如,下面的派生表查询无效,并产生一个错误:
select *from(select orderid,customerid from orders order by orderid)
as d
下面的视图也会产生错误
create view my_viewas
select *
from orders
order by orderid
在SQL中,表表达式中不允许使用带有ORDER BY子句的查询,而在T—SQL中却有一个例外(应用TOP选项)。
所以要记住,不要为表中的行假设任何特定的顺序。换句话说,除非你确定要有序行,否则不要指定ORDER BY 子句。排序是需要成本的,SQL Server需要执行有序索引扫描或使用排序运行符。
本文链接