ODBC(Open Database Connectivity,开放式数据库连接)提供了一种标准的应用程序编程接口(API),来访问各种数据库管理系统(DBMS)。这些API利用SQL来完成其大部分任务。ODBC本身也提供了对SQL语言的支持,用户可以直接将SQL语句送给ODBC。ODBC的设计目标是实现数据库访问的独立性和开放性,即与具体的编程语言、数据库系统和操作系统无关。大多数数据源都有ODBC驱动程序。ODBC是低级别高性能接口,专为关系数据存储而设计。
ODBC驱动程序附带sqlcmd和bcp等工具。使用sqlcmd实用工具可以运行Transact-SQL语句、系统过程和SQL脚本。bcp实用工具可以在Microsoft SQL Server实例和用户指定格式的数据文件间大批量复制数据。使用bcp实用工具可以将大量新行导入SQL Server表,或将表数据导出到数据文件。
Microsoft ODBC Driver for SQL Server是独立的ODBC驱动程序,是单个动态链接库(DLL),它包含对使用本机代码API连接到SQL Server的应用程序的运行时支持。
ODBC接口的使用涉及三大块:SQL语句、ODBC函数调用,以及C编程。
1.安装ODBC驱动程序:
(1).从 https://learn.microsoft.com 下载"Microsoft ODBC Driver 18 for SQL Server(x64)",下载后的文件名为msodbcsql.msi;
(2).双击msodbcsql.msi进行安装:注:默认仅安装客户端组件,安装Driver SDK需选定。
注:
Microsoft ODBC Driver 18 for SQL Server可与Microsoft ODBC Driver 17 for SQL Server并行安装。
对于安装了Driver 17.1.0.1或更低版本的用户,建议先手动将其卸载,然后再安装较新版本的驱动程序。
msodbcsql.h存放在C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Include目录下。
msodbcsql18.lib存放在C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Lib\x64目录下。
动态库msodbcsql18.dll存放在C:\Windows\System32目录下。
2.Windows10上配置ODBC数据源:数据源是数据的源以及访问该数据所需的连接信息。
(1).依次打开:控制面板 --> 管理工具:双击打开"ODBC 数据源(64位)",如下图所示:
(2).点击"添加",弹出的"创建新数据源"对话框中,选择SQL Server,点击完成,如下图所示:
注:
用户DSN(Data Source Name,数据源名称):仅对当前登录Windows的用户可见和可用。对个人开发或测试环境来说可能方便,因为它允许每个用户拥有自己独立的数据源配置。
系统DSN:对所有登录到Windows系统的用户都可见和可用。这意味着,一旦创建了系统DSN,任何登录到该系统的用户都可以访问和使用它。这对于需要多个用户共享同一数据源配置的环境(如生产环境)非常有用。
(3).名称:TestODBC;描述:测试ODBC;服务器:DESKTOP-P1TAS09;点击下一步,如下图所示:
(4).选择"使用用户输入登录ID和密码的SQL Server验证";登录ID:sa;密码:spring;点击下一页,如下图所示:
(5).勾选"更改默认的数据库为":Info;此数据库在 https://blog.csdn.net/fengbingchun/article/details/141021323 中有创建过程描述,点击下一页,如下图所示:
(6).勾选"更改SQL Server系统消息的语言为":Simplified Chinese;点击完成,如下图所示:
(7).点击"测试数据源",检验设置的ODBC信息是否有误,如下图所示:弹出对话框,提示"测试成功!",说明顺利连接,点击确定
(8).可以看到多了一条用户DSN,如下图所示:
3.通过ODBC API操作SQL Server数据库进行增删改查操作:
测试代码test_sqlserver_odbc.cpp如下:
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
namespace {
// display error/warning information
void handle_diagnostic_record(SQLHANDLE handle, SQLSMALLINT type, RETCODE code)
{
if (code == SQL_INVALID_HANDLE) {
std::cerr << "Error: Invalid handle" << std::endl;
return;
}
SQLSMALLINT number{ 0 };
constexpr SQLSMALLINT buffer_length{ 1024 };
SQLCHAR state[SQL_SQLSTATE_SIZE + 1] = { 0 }, message[buffer_length] = { 0 };
SQLINTEGER error{ 0 };
while (SQLGetDiagRec(type, handle, ++number, state, &error, message, buffer_length, nullptr) == SQL_SUCCESS) {
// hide data truncated ...
if (std::strncmp((char*)state, "01004", SQL_SQLSTATE_SIZE))
std::cerr << "Error: " << state << "," << message << ":" << error << std::endl;
}
}
// call ODBC functions and report an error on failure
void check_error(SQLHANDLE handle, SQLSMALLINT type, RETCODE code)
{
if (code != SQL_SUCCESS)
handle_diagnostic_record(handle, type, code);
if (code == SQL_ERROR) {
std::cerr << "Error: code: " << code << std::endl;
std::exit(1);
}
}
} // namespace
int test_sqlserver_odbc()
{
// allocate an environment
SQLHENV henv{ nullptr };
auto ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
if ( ret == SQL_ERROR) {
std::cerr << "Error: Unable to allocate an environment handle" << std::endl;
return -1;
}
// register this as an application that expects 3.x behavior, you must register something if you use AllocHandle
check_error(henv, SQL_HANDLE_ENV, SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,(SQLPOINTER)SQL_OV_ODBC3, 0));
// allocate a connection
SQLHDBC hdbc{ nullptr };
check_error(henv, SQL_HANDLE_ENV, SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc));
// connect to the driver.use the connection string if supplied on the input,otherwise let the driver manager prompt for input
//check_error(hdbc, SQL_HANDLE_DBC, SQLDriverConnect(hdbc, GetDesktopWindow(), (SQLCHAR*)"", SQL_NTS, nullptr, 0, nullptr, SQL_DRIVER_COMPLETE));
check_error(hdbc, SQL_HANDLE_DBC, SQLConnect(hdbc, (SQLCHAR*)"TestODBC", SQL_NTS, (SQLCHAR*)"sa", SQL_NTS, (SQLCHAR*)"spring", SQL_NTS));
SQLHSTMT hstmt{ nullptr };
check_error(hdbc, SQL_HANDLE_DBC, SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt));
// insert operation
SQLCHAR sql1[] = "INSERT INTO student VALUES(1111, 'Sam', 'man', 16, '2000-12-15 08:00')";
check_error(hstmt, SQL_HANDLE_STMT, SQLPrepare(hstmt, sql1, SQL_NTS));
check_error(hstmt, SQL_HANDLE_STMT, SQLExecute(hstmt));
SQLCHAR sql2[] = "INSERT INTO student VALUES(1112, '小李', '女', 22, '2010-12-15 08:00')";
check_error(hstmt, SQL_HANDLE_STMT, SQLPrepare(hstmt, sql2, SQL_NTS));
check_error(hstmt, SQL_HANDLE_STMT, SQLExecute(hstmt));
// delete operation
SQLCHAR sql5[] = "DELETE FROM student WHERE id = 1111";
check_error(hstmt, SQL_HANDLE_STMT, SQLPrepare(hstmt, sql5, SQL_NTS));
check_error(hstmt, SQL_HANDLE_STMT, SQLExecute(hstmt));
// query operation
SQLCHAR sql3[] = "SELECT * FROM student WHERE age > 20;";
check_error(hstmt, SQL_HANDLE_STMT, SQLExecDirect(hstmt, sql3, SQL_NTS));
int id{ 0 };
char name[32] = { 0 };
char sex[16] = { 0 };
int age{ 0 };
char brith[32] = { 0 };
SQLLEN len = SQL_NTS;
check_error(hstmt, SQL_HANDLE_STMT, SQLBindCol(hstmt, 1, SQL_INTEGER, &id, sizeof(id), 0));
check_error(hstmt, SQL_HANDLE_STMT, SQLBindCol(hstmt, 2, SQL_CHAR, &name, sizeof(name), &len));
check_error(hstmt, SQL_HANDLE_STMT, SQLBindCol(hstmt, 3, SQL_CHAR, &sex, sizeof(sex), &len));
check_error(hstmt, SQL_HANDLE_STMT, SQLBindCol(hstmt, 4, SQL_INTEGER, &age, sizeof(age), 0));
check_error(hstmt, SQL_HANDLE_STMT, SQLBindCol(hstmt, 5, SQL_CHAR, &brith, sizeof(brith), &len));
ret = SQLFetch(hstmt);
while (ret != SQL_NO_DATA) {
std::cout << id << "\t" << name << "\t" << sex << "\t" << age << "\t" << brith << std::endl;
id = 0;
std::memset(name, 0, 32);
std::memset(sex, 0, 16);
age = 0;
std::memset(brith, 0, 32);
ret = SQLFetch(hstmt);
}
// delete operation
if (hstmt)
check_error(hdbc, SQL_HANDLE_DBC, SQLFreeHandle(SQL_HANDLE_STMT, hstmt));
check_error(hdbc, SQL_HANDLE_DBC, SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt));
SQLCHAR sql4[] = "DELETE FROM student WHERE id = 1112";
check_error(hstmt, SQL_HANDLE_STMT, SQLPrepare(hstmt, sql4, SQL_NTS));
check_error(hstmt, SQL_HANDLE_STMT, SQLExecute(hstmt));
// update operation
SQLCHAR sql6[] = "UPDATE student SET name = '小田' WHERE id = 1003";
check_error(hstmt, SQL_HANDLE_STMT, SQLPrepare(hstmt, sql6, SQL_NTS));
check_error(hstmt, SQL_HANDLE_STMT, SQLExecute(hstmt));
// free ODBC handles
if (hstmt)
check_error(hdbc, SQL_HANDLE_DBC, SQLFreeHandle(SQL_HANDLE_STMT, hstmt));
if (hdbc) {
check_error(hdbc, SQL_HANDLE_DBC, SQLDisconnect(hdbc));
check_error(hdbc, SQL_HANDLE_DBC, SQLFreeHandle(SQL_HANDLE_DBC, hdbc));
}
if (henv)
check_error(hdbc, SQL_HANDLE_ENV, SQLFreeHandle(SQL_HANDLE_ENV, henv));
return 0;
}
执行插入后的结果如下图所示:id为1111和1112为新增加的数据
执行查询后的输出结果如下图所示:查询age>20的数据