ODBC 原理
ODBC 是 Open Database Connect 即开放数据库互连的简称,它是由 Microsoft 公司于 1991 年提出的一个用于访问数据库的统一界面标准,是应用程序和数据库系统之间的中间件。它通过使用相应应用平台上和所需数据库对应的驱动程序与应用程序的交互来实现对数据库的操作,避免了在应用程序中直接调用与数据库相关的操作,从而提供了数据库的独立性。
ODBC 主要由驱动程序和驱动程序管理器组成。驱动程序是一个用以支持 ODBC 函数调用的模块,每个驱动程序对应于相应的数据库,当应用程序从基于一个数据库系统移植到另一个时,只需更改应用程序中由 ODBC 管理程序设定的与相应数据库系统对应的别名即可。驱动程序管理器可链接到所有 ODBC 应用程序中,它负责管理应用程序中 ODBC 函数与 DLL 中函数的绑定。
ODBC 使用层次的方法来管理数据库,在数据库通信结构的每一层,对可能出现依赖数据库产品自身特性的地方, ODBC 都引入一个公共接口以解决潜在的不一致性,从而很好地解决了基于数据库系统应用程序的相对独立性,这也是 ODBC 一经推出就获得巨大成功的重要原因之一。
从结构上分, ODBC 分为单束式和多束式两类。
1. 单束式驱动程序
单束式驱动程序介于应用程序和数据库之间,像中介驱动程序一样数据提供一个统一的数据访问方式。当用户进行数据库操作时,应用程序传递一个 ODBC 函数调用给 ODBC 驱动程序管理器,由 ODBC API 判断该调用是由它直接处理并将结果返回还是送交驱动程序执行并将结果返回。由上可见,单束式驱动程序本身是一个数据库引擎,由它直接可完成对数据库的操作,尽管该数据库可能位于网络的任何地方。
2. 多束式驱动程序
多束式驱动程序负责在数据库引擎和客户应用程序之间传送命令和数据,它本身并不执行数据处理操作而用于远程操作的网络通信协议的一个界面。前端应用程序提出对数据库处理的请求,该请求转给 ODBC 驱动程序管理器,驱动程序管理器依据请求的情况,就地完成或传给多束驱动程序,多束式驱动程序将请求翻译为特定厂家的数据库通信接口(如 Oracle 的 SQLNet )所能理解的形式并交于接口去处理,接口把请求经网络传送给服务器上的数据引擎,服务器处理完后把结果发回给数据库通信接口,数据库接口将结果传给多束式 ODBC 驱动程序,再由驱动程序将结果传给应用程序。
很多程序员已经体会到了在 Windows 平台下的 ODBC 的益处,而在 Linux/Unix 下进行数据库编程的时候却不得不根据不同的数据库来选择特有的 API 进行编程,一旦数据库发生了改变,所有与这些 API 相关的程序都必须进行修改。其实在 Linux/Unix 下现在也有了自己的 ODBC ,可以使我们的数据库编程就像在 Windows 平台下一样简单。
下面我们开始介绍 Linux/Unix 下的 ODBC :
Linux/Unix 下 ODBC 的安装
方法一:
先下载最新的 unixODBC 源码包( http://www.unixodbc.org/unixODBC-2.2.1.tar.gz )放到 /usr/local 下,然后运行下述命令:
tar zxvf unixODBC-2.2.1.tar.gz
cd unixODBC-2.2.1
./configure --prefix=/usr/local/unixODBC-2.2.1 --includedir=/usr/include --libdir=/usr/lib -bindir=/usr/bin --sysconfdir=/etc
make && make install
安装成功后, unixODBC 所需的头文件都被安装到了 /usr/inlucde 下,编译好的库文件安装到了 /usr/lib 下,与 unixODBC 相关的可执行文件安装到了 /usr/bin 下,配置文件放到了 /etc 下。
方法二:
下载 rpm 包进行安装,我们这里以 Red Hat 7.3 为例:
unixODBC-2.2.0-5 RPM for i386 (安装包及源码包)
( ftp://speakeasy.rpmfind.net/linux/redhat/7.3/en/os/i386/RedHat/RPMS/unixODBC-2.2.0-5.i386.rpm 、 ftp://ftp.rpmfind.net/linux/redhat/7.3/en/os/i386/SRPMS/unixODBC-2.2.0-5.src.rpm )
unixODBC-devel-2.2.0-5 RPM for i386
( ftp://speakeasy.rpmfind.net/linux/redhat/7.3/en/os/i386/RedHat/RPMS/unixODBC-devel-2.2.0-5.i386.rpm )
直接将 unixODBC-2.2.0-5.i386.rpm 和 unixODBC-devel-2.2.0-5.i386.rpm 装入系统就可以了,命令如下:
rpm -ivh unixODBC-2.2.0-5.i386.rpm
rpm -ivh unixODBC-devel-2.2.0-5.i386.rpm
安装好以后,所需的各个部分与上面所列的位置相同。
Linux/Unix 下 ODBC 的配置
运行 ODBCConfig 程序(在 /usr/bin 下),如下图:
图一: ODBCConfig 主窗口
和 Windows 下的 ODBC 设置窗口是不是很像?我想大家都能看懂吧。
第一步:安装数据库的 ODBC 驱动程序
Drivers 这一栏中用来设置数据库的驱动程序,点击 Add 按钮,会出现下图:
图二: ODBCConfig Driver Properties 窗口
Name 一栏填入数据库驱动的名称, Description 是数据库驱动的描述, Driver 是用来选择数据库驱动程序的, Setup 是用来选择数据库驱动安装程序的,如果你是按照上述安装方法安装的,这些程序都放在 /usr/lib 下,下面是数据库驱动程序的列表:
数据库 | 数据库驱动程序 | 数据库驱动安装程序 |
TXT | libodbctxt.so | libodbctxtS.so |
NNTP | libnn.so | libodbcnnS.so |
MiniSQL | libodbcmini.so | libodbcminiS.so |
PostgreSQL | libodbcpsql.so | libodbcpsqlS.so |
MySQL | (注释) | libodbcmyS.so |
Sybase/MS SQL | (注释) | libtdsS.so |
Oracle | (注释) | liboraodbcS.so |
注释:
MySQL 、 Sybase/MS SQL 和 Oracle 的数据库驱动可以在下列网址找到:
MySQL http://www.unixodbc.org/myodbc.html
Sybase/MS SQL http://www.freetds.org
Oracle http://www.easysoft.org
MySQL 的驱动程序 MyODBC-2.50.39-4 RPM for i386 以及源码包:
ftp://speakeasy.rpmfind.net/linux/redhat/7.3/en/os/i386/RedHat/RPMS/MyODBC-2.50.39-4.i386.rpm
ftp://ftp.redhat.com/pub/redhat/linux/7.3/en/os/i386/SRPMS/MyODBC-2.50.39-4.src.rpm
选择好驱动程序之后,点击 "√" 保存退出。
第二步:设置 DSN
DSN 分为 User DSN 、 System DSN 和 File DSN 三种,我们以 System DSN 为例。选中 System DSN 一栏以后,点击 Add… 按钮就会见到下图:
图三:创建 DSN 选择数据库驱动
列表中会列出你已经安装好的数据库驱动程序,我这里只装了 MySQL 和 PostgreSQL ,然后选择你所要使用的驱动程序,然后点击 OK 就会出现下图:
图四: DSN 的设置
我这里使用的是 MySQL 的数据库驱动,不同的数据库,这个窗口的内容会有所不同。 Name 是数据源的名称, Description 是描述, Server 可以选择服务器,如果本机启动了 MySQL 就可以选择 localhost ,如果 Port 和 Socket 有特殊要求,再根据实际情况进行修改, Database 是用来选择数据库的,下拉菜单不一定包含所有的数据库,你可以把自己已经创建好的数据库名称填写在这里。都配置好之后,点击 "√" 保存退出。
这样 Linux/Unix 下的 ODBC 数据源就已经设置好了,大家还可以在 ODBCConfig 程序的 Status 栏中查看 ODBC 的使用情况,在 Advanced 栏中设置是否做日志或者启动连接池,在 About 栏中,有一个 Linux/Unix ODBC 的示意图,在 Credits 按钮中可以看到所有开发者的名字的列表。 ODBCConfig 程序中所有有关数据库驱动程序的信息被放在 odbcinst.ini (在 /etc 下)文件中,有关 DSN 的信息被放在 odbc.ini (在 /etc 下)文件中,大家有兴趣的话,可以自己去观察一下。
第三步:使用 DataManager 程序浏览数据库
运行 DataManager 程序之后就可以查看 Drivers 、 System DSN 和 User DSN 这几项内容,,在浏览数据库的时候,可以在右面的 SQL 栏中输入 SQL 语句,然后点击人形按钮就可以运行 SQL 语句,运行结果会在 Results 一栏中显示出来,具体情况可以见下图:
图五:使用 DataManager 浏览数据库
第四步:使用 isql 程序查看数据库
unixODBC 还提供了命令台下查看数据库的程序,这就是 isql ,用法如下:
isql DSN [UID [PWD]] [options]
DSN 数据源名称
UID 用户 ID
PWD 用户密码
Options :
-b 批处理,没有提示符的模式
-dx 设置列之间的分隔符为 x
-w 将查询结果输出为 HTML 格式
-c 第一行输出列名
--version 输出 isql 的版本号
Linux/Unix 下 ODBC 的编程
1 、使用 unixODBC 提供的 ODBC API 进行编程:
在进行编程之前,我们来看一下 ODBC API 中的常用数据类型与我们在 C 语言中使用的数据类型的对应关系:
类型标识符 | ODBC数据类型 | C数据类型 |
SQL_C_CHAR | SQLCHAR * | unsigned char * |
SQL_C_SSHORT | SQLSMALLINT | short int |
SQL_C_USHORT | SQLUSMALLINT | unsigned short int |
SQL_C_SLONG | SQLINTEGER | long int |
SQL_C_FLOAT | SQLREAL | float |
SQL_C_DOUBLE | SQLDOUBLE, SQLFLOAT | double |
SQL_C_BINARY | SQLCHAR * | unsigned char * |
SQL_C_TYPE_DATE | SQL_DATE_STRUCT | struct tagDATE_STRUCT {SQLSMALLINT year; SQLUSMALLINT month; SQLUSMALLINT day; } DATE_STRUCT; |
SQL_C_TYPE_TIME | SQL_TIME_STRUCT | struct tagTIME_STRUCT {SQLUSMALLINT hour; SQLUSMALLINT minute; SQLUSMALLINT second; } TIME_STRUCT; |
我们这里使用的数据库名称为 test ( DSN ),这个 DSN 使用的用户名是 root ,密码为空,表的名称是 web ,字段情况如下:
字段名 | 数据类型 |
id | integer |
name | char(40) |
size | integer |
第一:设定 ODBC 环境句柄并设置参数
首先我们需要声明一个 ODBC 环境句柄( SQLHENV ),它可以用来获得有关的 ODBC 环境信息,我们需要调用 SQLAllocHandle ( SQL_HANDLE_ENV, SQL_NULL_HANDLE, &V_OD_Env ) 来获得这个句柄, V_OD_Env 就是要分配的 SQLHENV 类型的环境句柄。分配好句柄之后,你给它需要设定所使用的 ODBC 版本,你可以调用 SQLSetEnvAttr ( V_OD_Env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0 ) , SQL_ATTR_ODBC_VERSION 是存放你定义的 ODBC 版本号的变量, SQL_OV_ODBC3 则说明你的程序使用的是 ODBC 3.0 。
第二:设定连接句柄并设置超时参数
我们需要声明一个连接句柄( SQLHDBC ),用来存放数据库连接信息的,调用 SQLAllocHandle ( SQL_HANDLE_DBC, V_OD_Env, &V_OD_hdbc ) 获得连接句柄, V_OD_hdbc 就是要分配的 SQLHDBC 类型的连接句柄。分配好之后,我们可以调用 SQLSetConnectAttr ( V_OD_hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0 ) 来设定连接超时参数。
第三:连接数据库
调用 SQLConnect ( V_OD_hdbc, (SQLCHAR*) "Test", SQL_NTS, (SQLCHAR*) "root", SQL_NTS, (SQLCHAR*) "", SQL_NTS ) 连接我前面提到的数据库,需要设定三个参数,就是数据库名称、用户名和密码(因为我的数据库密码为空,所以这里的密码也为空),后面的 SQL_NTS 的位置应该写入这些参数的长度,如果写的是 SQL_NTS 就是让 SQLConnect 来决定参数的长度。
第四:分配 SQL 语句的句柄并进行查询:
需要声明一个 SQL 语句的句柄( SQLHSTMT ),用来存放 SQL 语句信息的,调用 SQLAllocHandle ( SQL_HANDLE_STMT, V_OD_hdbc, &V_OD_hstmt ) 来获得这个句柄, V_OD_hstmt 就是我们要分配的 SQLHSTMT 类型的 SQL 语句句柄。
我们的查询语句是:
SELECT name, id FROM web ORDER BY id
执行这条查询语句之后,查询结果可能有很多行,但每行只有两列,分别对应 name 和 id ,它们的数据类型为 integer 和 char* ,在 ODBC 中的数据类型标识符为 SQL_C_ULONG 和 SQL_C_CHAR 。我们需要先声明这样的两个变量来存贮查询结果:
SQLINTEGER V_OD_id;
char V_OD_buffer[200];
然后我们需要使用 SQLBindCol 函数把查询结果和我们定义的变量进行绑定:
SQLBindCol(V_OD_hstmt,1,SQL_C_CHAR, &V_OD_buffer,150,&V_OD_err);
SQLBindCol(V_OD_hstmt,2,SQL_C_ULONG,&V_OD_id,150,&V_OD_err);
这里的 V_OD_err 是用来存放错误信息编号的变量,类型也是 SQLINTEGER 。
接下来,我们调用 SQLExecDirect 来进行查询:
SQLExecDirect ( V_OD_hstmt, "SELECT dtname,iduser FROM web order by iduser", SQL_NTS );
我们可以用 SQLNumResultCols ( V_OD_hstmt, &V_OD_colanz ) 来获得结果的列数,也可以用 SQLRowCount( V_OD_hstmt, &V_OD_rowanz ) 来获得结果的条数, V_OD_colanz 和 V_OD_rowanz 分别存储相应的结果,类型分别为 SQLSMALLINT 和 SQLINTEGER 。
在读取结果之前,我们需要调用 SQLFetch ( V_OD_hstmt ) 语句,这个语句可以用来获得第一条结果也可以用来都下一条,有点像 next 的感觉。然后我们就可以在 V_OD_id 和 V_OD_buffer 里面获得每条记录的结果了。
第五:关于关闭连接和释放句柄
关闭数据库的连接,调用 SQLDisconnect ( V_OD_hdbc ) 就可以了,但在关闭数据库之前需要先释放 SQL 语句的句柄,而且在关闭数据库之后应该释放连接句柄和 ODBC 环境句柄,语句如下(按正常的顺序):
SQLFreeHandle(SQL_HANDLE_STMT,V_OD_hstmt);
SQLDisconnect(V_OD_hdbc);
SQLFreeHandle(SQL_HANDLE_DBC,V_OD_hdbc);
SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);
第六:关于上述情况中的错误信息处理
我们需要定义两个变量:
long V_OD_erg;
SQLINTEGER V_OD_err;
SQLAllocHandle 、 SQLSetEnvAttr 、 SQLSetConnectAttr 、 SQLConnect 、 SQLExecDirect 、 SQLNumResultCols 和 SQLRowCount 的调用结果都可以用 V_OD_erg 来存储, V_OD_err 可以获得 SQLBindCol 中的错误信息。
第七:获得本机的 DSN 信息
我们可以在声明 SQLHENV 句柄之后,使用 SQLDataSources 函数来获得本机的 DSN 信息。程序如下:
void OD_ListDSN(void)
{
char l_dsn[100],l_desc[100];
short int l_len1,l_len2,l_next;
l_next=SQL_FETCH_FIRST;
while( SQLDataSources(V_OD_Env,l_next,l_dsn, sizeof(l_dsn),
&l_len1, l_desc, sizeof(l_desc), &l_len2) == SQL_SUCCESS)
{
printf("Server=(%s) Beschreibung=(%s)\n",l_dsn,l_desc);
l_next=SQL_FETCH_NEXT;
}
}
l_next 变量是用来指定我们所要获得的 DSN 的类别:
SQL_FETCH_FIRST | 设定SQLDataSources()函数找到第一个可用的数据源(可以是User DSN,也可以是Systerm DSN) |
SQL_FETCH_FIRST_USER | 设定SQLDataSources()函数找到第一个User DSN |
SQL_FETCH_FIRST_SYSTEM | 设定SQLDataSources()函数找到第一个System DSN |
SQL_FETCH_NEXT | 找到下一个数据源,至于数据源类型则要根据前面的定义 |
到这里,我们在 Unix 的 C 语言下面进行 ODBC 编程已经讲完,上述 ODBC API 需要引用以下几个头文件(这些文件已经安装到 /usr/include 下了):
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
另外如果大家使用 GTK 进行编程,由于到目前为止 GTK 还没有加入专门处理数据库的部件,所以大家可以在 GTK 中调用上述的 ODBC API 即可。
这里附上例程供大家参考学习:
/* odbc.c
testing unixODBC
*/
#include <stdlib.h>
#include <stdio.h>
#include <odbc/sql.h>
#include <odbc/sqlext.h>
#include <odbc/sqltypes.h>
SQLHENV V_OD_Env; // Handle ODBC environment
long V_OD_erg; // result of functions
SQLHDBC V_OD_hdbc; // Handle connection
char V_OD_stat[10]; // Status SQL
SQLINTEGER V_OD_err,V_OD_rowanz,V_OD_id;
SQLSMALLINT V_OD_mlen,V_OD_colanz;
char V_OD_msg[200],V_OD_buffer[200];
int main(int argc,char *argv[])
{
// 1. allocate Environment handle and register version
V_OD_erg=SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&V_OD_Env);
if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))
{
printf("Error AllocHandle\n");
exit(0);
}
V_OD_erg=SQLSetEnvAttr(V_OD_Env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))
{
printf("Error SetEnv\n");
SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);
exit(0);
}
// 2. allocate connection handle, set timeout
V_OD_erg = SQLAllocHandle(SQL_HANDLE_DBC, V_OD_Env, &V_OD_hdbc);
if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))
{
printf("Error AllocHDB %d\n",V_OD_erg);
SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);
exit(0);
}
SQLSetConnectAttr(V_OD_hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0);
// 3. Connect to the datasource "web"
V_OD_erg = SQLConnect(V_OD_hdbc, (SQLCHAR*) "Test", SQL_NTS,
(SQLCHAR*) "root", SQL_NTS,
(SQLCHAR*) "", SQL_NTS);
if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))
{
printf("Error SQLConnect %d\n",V_OD_erg);
SQLGetDiagRec(SQL_HANDLE_DBC, V_OD_hdbc,1,
V_OD_stat, &V_OD_err,V_OD_msg,100,&V_OD_mlen);
printf("%s (%d)\n",V_OD_msg,V_OD_err);
SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);
exit(0);
}
printf("Connected !\n");
V_OD_erg=SQLAllocHandle(SQL_HANDLE_STMT, V_OD_hdbc, &V_OD_hstmt);
if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))
{
printf("Fehler im AllocStatement %d\n",V_OD_erg);
SQLGetDiagRec(SQL_HANDLE_DBC, V_OD_hdbc,1, V_OD_stat,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
printf("%s (%d)\n",V_OD_msg,V_OD_err);
SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);
exit(0);
}
SQLBindCol(V_OD_hstmt,1,SQL_C_CHAR, &V_OD_buffer,150,&V_OD_err);
SQLBindCol(V_OD_hstmt,2,SQL_C_ULONG,&V_OD_id,150,&V_OD_err);
V_OD_erg=SQLExecDirect(V_OD_hstmt,"SELECT dtname,iduser FROM web order by iduser",SQL_NTS);
if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))
{
printf("Error in Select %d\n",V_OD_erg);
SQLGetDiagRec(SQL_HANDLE_DBC, V_OD_hdbc,1, V_OD_stat,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
printf("%s (%d)\n",V_OD_msg,V_OD_err);
SQLFreeHandle(SQL_HANDLE_STMT,V_OD_hstmt);
SQLFreeHandle(SQL_HANDLE_DBC,V_OD_hdbc);
SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);
exit(0);
}
V_OD_erg=SQLNumResultCols(V_OD_hstmt,&V_OD_colanz);
if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))
{
SQLFreeHandle(SQL_HANDLE_STMT,V_OD_hstmt);
SQLDisconnect(V_OD_hdbc);
SQLFreeHandle(SQL_HANDLE_DBC,V_OD_hdbc);
SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);
exit(0);
}
printf("Number of Columns %d\n",V_OD_colanz);
V_OD_erg=SQLRowCount(V_OD_hstmt,&V_OD_rowanz);
if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))
{
printf("Number of RowCount %d\n",V_OD_erg);
SQLFreeHandle(SQL_HANDLE_STMT,V_OD_hstmt);
SQLDisconnect(V_OD_hdbc);
SQLFreeHandle(SQL_HANDLE_DBC,V_OD_hdbc);
SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);
exit(0);
}
printf("Number of Rows %d\n",V_OD_rowanz);
V_OD_erg=SQLFetch(V_OD_hstmt);
while(V_OD_erg != SQL_NO_DATA)
{
printf("Result: %d %s\n",V_OD_id,V_OD_buffer);
V_OD_erg=SQLFetch(V_OD_hstmt);
} ;
SQLFreeHandle(SQL_HANDLE_STMT,V_OD_hstmt);
SQLDisconnect(V_OD_hdbc);
SQLFreeHandle(SQL_HANDLE_DBC,V_OD_hdbc);
SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);
return(0);
}
2 . QT 下进行 ODBC 编程
QT 3.0 提供了 Data Table 、 Data Browser 和 Data View 三个与数据库相关的控件。你可以在 QT 的 Project 设置你要连接的数据库, Driver 一栏中选择 QODBC3 即可,其它选项你一看就明白了。上述的三个数据库控件的使用方法可以参见 QT 中相应文档,也很好使用的。
About Me
........................................................................................................................ ● 本文作者:小麦苗,部分内容整理自网络,若有侵权请联系小麦苗删除 ● 本文在itpub( http://blog.itpub.net/26736162 )、博客园( http://www.cnblogs.com/lhrbest )和个人weixin公众号( xiaomaimiaolhr )上有同步更新 ● 本文itpub地址: http://blog.itpub.net/26736162 ● 本文博客园地址: http://www.cnblogs.com/lhrbest ● 本文pdf版、个人简介及小麦苗云盘地址: http://blog.itpub.net/26736162/viewspace-1624453/ ● 数据库笔试面试题库及解答: http://blog.itpub.net/26736162/viewspace-2134706/ ● DBA宝典今日头条号地址: http://www.toutiao.com/c/user/6401772890/#mid=1564638659405826 ........................................................................................................................ ● QQ群号: 230161599 (满) 、618766405 ● weixin群:可加我weixin,我拉大家进群,非诚勿扰 ● 联系我请加QQ好友 ( 646634621 ) ,注明添加缘由 ● 于 2019-05-01 06:00 ~ 2019-05-30 24:00 在魔都完成 ● 最新修改时间:2019-05-01 06:00 ~ 2019-05-30 24:00 ● 文章内容来源于小麦苗的学习笔记,部分整理自网络,若有侵权或不当之处还请谅解 ● 版权所有,欢迎分享本文,转载请保留出处 ........................................................................................................................ ● 小麦苗的微店 : https://weidian.com/s/793741433?wfr=c&ifr=shopdetail ● 小麦苗出版的数据库类丛书 : http://blog.itpub.net/26736162/viewspace-2142121/ ● 小麦苗OCP、OCM、高可用网络班 : http://blog.itpub.net/26736162/viewspace-2148098/ ● 小麦苗腾讯课堂主页 : https://lhr.ke.qq.com/ ........................................................................................................................ 使用 weixin客户端 扫描下面的二维码来关注小麦苗的weixin公众号( xiaomaimiaolhr )及QQ群(DBA宝典)、添加小麦苗weixin, 学习最实用的数据库技术。 ........................................................................................................................ |
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/26736162/viewspace-2644122/,如需转载,请注明出处,否则将追究法律责任。