ODBC 是一種原生資料存取應用程式設計介面(API),用於使用 C 和 C++ 撰寫的應用程式。 Microsoft ODBC 的 SQL Server 驅動程式可連接 SQL Server、Azure SQL Database、Azure SQL 受控執行個體、Azure Synapse Analytics 以及 Microsoft Fabric 中的 SQL 資料庫。 關於每個驅動版本所支援的資料庫版本,請參見 SQL 版本相容性。
你的應用程式呼叫 ODBC 驅動程式管理器,該管理器會載入驅動程式並將 ODBC 呼叫傳給它。 在 Windows 上使用 Windows 的 ODBC 驅動程式管理器,Linux 和 macOS 則使用 unixODBC。
可以使用 ODBC 的其他語言包括 COBOL、Perl、PHP 和 Python。 ODBC 廣泛應用於資料整合情境,Microsoft PHP for SQL Server 驅動程式即基於此驅動程式。
sqlcmd 的 ODBC 變體與 bcp 工具使用此驅動程式,但它們是分別安裝的:Linux 與 macOS 上的 mssql-tools18 套件,以及 Windows 上的 Microsoft 命令列工具。
sqlcmd 的 Go 版本並未使用這個驅動程式。 使用 sqlcmd 執行 Transact-SQL(T-SQL)語句、系統程序及腳本檔案。 使用 bcp 在 SQL Server 實例與資料檔案之間批量複製資料,無論雙向皆然。
選擇你的起點
- 要安裝驅動程式,請前往 Windows 的系統需求、安裝與驅動程式檔案,或在 Linux 安裝 ODBC 驅動程式,在 macOS 安裝 ODBC 驅動程式,然後安裝 unixODBC 驅動程式管理器。
- 要寫第一個應用程式,請到「使用 C++ 連線到資料庫並進行查詢」。 對於資料來源名稱(DSN)和 連接字串 選項,請使用 DSN 和 連接字串 關鍵字及屬性。 如果你的編譯器找不到標頭,或是你的建置無法連結,請用 ODBC 驅動程式進入 Develop C 和 C++ 應用程式。
- 若要使用無密碼驗證連線至 Azure SQL,請前往 搭配 ODBC 驅動程式使用 Microsoft Entra ID。
- 要讓現有應用程式對暫時性故障具備韌性,請參考 Connection 韌性 與 高可用性與災難復原。
- 要從版本 17 升級,請前往 主要版本差異 與 連線加密故障排除。
- 要診斷連線或查詢問題,請前往連線加密故障排除和已知問題(Linux 和 macOS)。
Azure SQL 的生產環境基準
將這個完整的 C++17 程式作為 Azure 託管應用程式的起點。 它會連接、執行 SELECT 1、檢查結果,然後釋放 ODBC 的句柄。 在建置前先安裝 Driver 18 和平台的 ODBC 開發檔案。 關於前置條件,請參見 使用 ODBC 驅動程式開發 C 與 C++ 應用程式。
在應用程式的環境中將ODBC_CONNECTION_STRING設為完整的 連接字串。 以下範例使用系統指派的受控識別。 主機必須已啟用該身分識別,且資料庫必須授權它存取。 關於主機需求、使用者指派身分識別,以及本機開發驗證,請參閱 使用 Microsoft Entra ID 搭配 ODBC 驅動程式。
Driver={ODBC Driver 18 for SQL Server};Server=tcp:<server>.database.windows.net,1433;Database=<database>;Authentication=ActiveDirectoryMsi;Encrypt=strict;ConnectRetryCount=3;ConnectRetryInterval=2;
Encrypt=strict 需要一個支援表格資料串流(TDS)8.0 的端點。 否則,請使用 Encrypt=yes;TrustServerCertificate=no。 設定一個客戶端信任且名稱與伺服器相符的憑證。 關於設定與驗證,請參見 連線加密故障排除。
避免憑證出現在原始檔案和 shell 歷史中。 關於設定環境變數的指令,請參見「設定連線」。 在 Linux 和 macOS 上,以 UTF-8 提供其值。 將以下程式儲存為 odbc-baseline.cpp:
#ifdef _WIN32
#include <windows.h>
#else
#include <iconv.h>
#endif
#include <sql.h>
#include <sqlext.h>
#include <chrono>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <random>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>
struct Handles {
SQLHENV env = SQL_NULL_HENV;
SQLHDBC dbc = SQL_NULL_HDBC;
SQLHSTMT stmt = SQL_NULL_HSTMT;
bool connected = false;
~Handles() {
if (stmt) SQLFreeHandle(SQL_HANDLE_STMT, stmt);
if (connected) SQLDisconnect(dbc);
if (dbc) SQLFreeHandle(SQL_HANDLE_DBC, dbc);
if (env) SQLFreeHandle(SQL_HANDLE_ENV, env);
}
};
bool logDiagnostics(SQLSMALLINT type, SQLHANDLE handle) {
bool retry = false;
bool permanent = false;
for (SQLSMALLINT record = 1; ; ++record) {
SQLWCHAR state[6] = {};
SQLWCHAR message[1024] = {};
SQLINTEGER native = 0;
SQLRETURN rc = SQLGetDiagRecW(type, handle, record, state, &native,
message, 1024, nullptr);
if (rc == SQL_NO_DATA) break;
if (!SQL_SUCCEEDED(rc)) {
std::cerr << "event=diagnostics_failed rc=" << rc << '\n';
return false;
}
std::string sqlstate;
for (int i = 0; i < 5; ++i) {
sqlstate += static_cast<char>(state[i]);
}
std::cerr << "event=odbc_diagnostic sqlstate=" << sqlstate
<< " native_error=" << native << '\n';
permanent |= sqlstate.substr(0, 2) == "28" ||
sqlstate.substr(0, 2) == "IM" ||
sqlstate == "HY024" || sqlstate == "HY092";
retry |= sqlstate == "08S01" || sqlstate == "HYT00" ||
sqlstate == "HYT01";
switch (native) {
case 10928: case 10929: case 10936:
case 40197: case 40501: case 40613:
case 49918: case 49919: case 49920:
retry = true;
}
}
return retry && !permanent;
}
void requireSuccess(SQLRETURN rc, SQLSMALLINT type, SQLHANDLE handle,
const char* operation) {
if (rc == SQL_SUCCESS) return;
logDiagnostics(type, handle);
if (!SQL_SUCCEEDED(rc)) throw std::runtime_error(operation);
}
std::vector<SQLWCHAR> readConnectionString() {
static_assert(sizeof(SQLWCHAR) == 2, "Use a driver manager with 16-bit SQLWCHAR");
#ifdef _WIN32
size_t length = 0;
if (_wgetenv_s(&length, nullptr, 0, L"ODBC_CONNECTION_STRING") != 0 ||
length == 0) {
throw std::runtime_error("ODBC_CONNECTION_STRING is required");
}
std::vector<wchar_t> raw(length);
if (_wgetenv_s(&length, raw.data(), raw.size(), L"ODBC_CONNECTION_STRING") != 0) {
throw std::runtime_error("Cannot read ODBC_CONNECTION_STRING");
}
std::vector<SQLWCHAR> result(raw.begin(), raw.end() - 1);
#else
const char* raw = std::getenv("ODBC_CONNECTION_STRING");
if (!raw) throw std::runtime_error("ODBC_CONNECTION_STRING is required");
iconv_t converter = iconv_open("UTF-16LE", "UTF-8");
if (converter == reinterpret_cast<iconv_t>(-1)) {
throw std::runtime_error("Cannot create UTF-8 converter");
}
size_t inputBytes = std::strlen(raw);
std::vector<SQLWCHAR> result(inputBytes + 1);
char* input = const_cast<char*>(raw);
char* output = reinterpret_cast<char*>(result.data());
size_t outputBytes = result.size() * sizeof(SQLWCHAR);
const size_t capacity = outputBytes;
size_t status = iconv(converter, &input, &inputBytes, &output, &outputBytes);
int closeStatus = iconv_close(converter);
if (status == static_cast<size_t>(-1) || inputBytes != 0 || closeStatus != 0) {
throw std::runtime_error("ODBC_CONNECTION_STRING must be valid UTF-8");
}
result.resize((capacity - outputBytes) / sizeof(SQLWCHAR));
#endif
if (result.empty() || result.size() >= 32767) {
throw std::runtime_error("Invalid ODBC_CONNECTION_STRING length");
}
result.push_back(0);
return result;
}
void connectWithRetry(SQLHDBC dbc, std::vector<SQLWCHAR>& connectionString) {
constexpr int maxAttempts = 4;
std::mt19937 random{std::random_device{}()};
std::uniform_int_distribution<int> jitter{0, 250};
for (int attempt = 1; attempt <= maxAttempts; ++attempt) {
requireSuccess(SQLSetConnectAttr(
dbc, SQL_ATTR_LOGIN_TIMEOUT,
reinterpret_cast<SQLPOINTER>(std::uintptr_t{30}), 0),
SQL_HANDLE_DBC, dbc, "Set login timeout");
SQLRETURN rc = SQLDriverConnectW(
dbc, nullptr, connectionString.data(), SQL_NTS,
nullptr, 0, nullptr, SQL_DRIVER_NOPROMPT);
if (SQL_SUCCEEDED(rc)) {
if (rc == SQL_SUCCESS_WITH_INFO) logDiagnostics(SQL_HANDLE_DBC, dbc);
std::cout << "event=connected attempt=" << attempt << '\n';
return;
}
bool transient = logDiagnostics(SQL_HANDLE_DBC, dbc);
if (!transient || attempt == maxAttempts) {
throw std::runtime_error("Connect failed");
}
auto delay = std::chrono::milliseconds{
(1 << (attempt - 1)) * 10000 + jitter(random)};
std::cerr << "event=connect_retry attempt=" << attempt
<< " delay_ms=" << delay.count() << '\n';
std::this_thread::sleep_for(delay);
}
}
int main() {
try {
auto connectionString = readConnectionString();
Handles handles;
if (!SQL_SUCCEEDED(SQLAllocHandle(
SQL_HANDLE_ENV, SQL_NULL_HANDLE, &handles.env))) {
throw std::runtime_error("Allocate environment");
}
requireSuccess(SQLSetEnvAttr(
handles.env, SQL_ATTR_ODBC_VERSION,
reinterpret_cast<SQLPOINTER>(std::uintptr_t{SQL_OV_ODBC3}), 0),
SQL_HANDLE_ENV, handles.env, "Set ODBC version");
requireSuccess(SQLAllocHandle(SQL_HANDLE_DBC, handles.env, &handles.dbc),
SQL_HANDLE_ENV, handles.env, "Allocate connection");
connectWithRetry(handles.dbc, connectionString);
handles.connected = true;
requireSuccess(SQLAllocHandle(SQL_HANDLE_STMT, handles.dbc, &handles.stmt),
SQL_HANDLE_DBC, handles.dbc, "Allocate statement");
requireSuccess(SQLSetStmtAttr(
handles.stmt, SQL_ATTR_QUERY_TIMEOUT,
reinterpret_cast<SQLPOINTER>(std::uintptr_t{10}), 0),
SQL_HANDLE_STMT, handles.stmt, "Set query timeout");
SQLWCHAR query[] = {'S', 'E', 'L', 'E', 'C', 'T', ' ', '1', 0};
requireSuccess(SQLExecDirectW(handles.stmt, query, SQL_NTS),
SQL_HANDLE_STMT, handles.stmt, "Execute probe");
requireSuccess(SQLFetch(handles.stmt),
SQL_HANDLE_STMT, handles.stmt, "Fetch probe");
SQLINTEGER value = 0;
SQLLEN indicator = 0;
requireSuccess(SQLGetData(handles.stmt, 1, SQL_C_SLONG,
&value, sizeof(value), &indicator),
SQL_HANDLE_STMT, handles.stmt, "Read probe");
if (indicator == SQL_NULL_DATA || value != 1) {
throw std::runtime_error("Unexpected probe result");
}
std::cout << "event=ready result=" << value << '\n';
return 0;
} catch (const std::exception& error) {
std::cerr << "event=failed operation=\"" << error.what() << "\"\n";
return 1;
}
}
程式會請求最多四次連線嘗試中,每次的連線逾時為 30 秒,並請求 10 秒查詢逾時。 在連線暫時失敗的嘗試之間,會等待10、20和40秒,每次隨機抖動時間介於0到250毫秒。 這些限制並不對驗證設定一個固定的整體期限。 範例分類了選擇的暫態連線錯誤;請根據你的工作負載調整策略,使用 Azure SQL 暫態故障錯誤目錄。 它不會重試應用程式指令或寫入作業。
ConnectRetryCount 和 ConnectRetryInterval 會為閒置時中斷的合格連線設定連線韌性。 它們不會重試初始連線,也不會重播應用程式指令。 讓閒置恢復的延遲時間比查詢逾時短。 範例日誌記錄 SQLSTATE 值及原生錯誤編號,而不是連接字串或完整診斷訊息。 如果你需要訊息文字來排解故障,請將其記錄在受保護的診斷日誌中。
建立並執行基準
使用與你應用程式處理器架構相符的編譯器環境。 在 Windows 上,請在 Visual Studio 的開發者 PowerShell 中執行這些指令。 在 Linux 和 macOS 上,使用包含編譯器和 unixODBC 開發檔案的 shell。
cl /nologo /std:c++17 /EHsc /W4 odbc-baseline.cpp odbc32.lib
.\odbc-baseline.exe
第一次成功連線時,會顯示以下輸出。 還可能出現額外的診斷紀錄。
event=connected attempt=1
event=ready result=1
完成後清除 ODBC_CONNECTION_STRING:在 PowerShell 使用 Remove-Item Env:\ODBC_CONNECTION_STRING,在 Bash 或 Zsh 使用 unset ODBC_CONNECTION_STRING。
對於可用性群組監聽器和故障切換設定,請使用 高可用性和災難復原。
主要功能
- 跨平台:Windows、Linux 和 macOS 使用相同的 API。
- Microsoft Entra ID 認證:管理式身分識別、服務主體與整合式認證,以及 Windows 上的互動式認證。 可用的方法取決於主機與資料庫的配置。
- 嚴格加密:在第 18 版及更新版本中,TDS 8.0 連線會採用完整的憑證驗證。
- 始終加密:用戶端加密以處理敏感欄位,並支援自訂金鑰儲存提供者。
- 連線復原能力:可透明地重新建立在閒置時中斷的連線。
-
高可用性:可用性群組監聽器支援。
MultiSubnetFailover - 資料分類:分類欄位的敏感性元資料。
- 向量資料型別:原生支援 向量 型別。
- 分散式交易:透過 Microsoft Distributed Transaction Coordinator(MSDTC)支援 XA 交易。
- 配套工具: sqlcmd 和 bcp,分別安裝。
開始
| 文章 | 說明 |
|---|---|
| 下載 SQL Server 的 ODBC 驅動程式 | 每個支援驅動版本的安裝程式和套件下載,涵蓋三個平台。 |
| 使用 ODBC 驅動程式開發 C 與 C++ 應用程式 | 要包含哪些標頭、順序、連結哪些函式庫,以及如何在非同步執行與執行緒之間選擇。 |
| 使用 C++ 連接並查詢資料庫 | 完整的 C++ 範例,能連接、執行查詢並讀取結果,讓你能從頭到尾確認你的設定。 |
| 支援生命週期 | 哪些驅動程式版本仍受支援,以及每個版本的停止支援日期。 |
| 主要版本差異 | 從版本 17 移轉到版本 18 時,哪些地方會失效,先從預設加密設定的變更談起。 |
安裝驅動程式
| 文章 | 說明 |
|---|---|
| 系統需求、安裝與驅動程式檔案(Windows) | 支援的 Windows 版本、用於靜默部署的安裝程式命令列,以及各驅動程式檔案在磁碟上的存放位置。 |
| 系統需求(Linux 與 macOS) | 每個驅動程式版本支援哪些 Linux 發行版和 macOS 版本,以及與 SQL Server 版本的相容性。 |
| 在 Linux 上安裝 ODBC 驅動程式 | Alpine、Debian、Red Hat、SUSE、Ubuntu 和 Azure Linux 的套件管理步驟,以及離線安裝和驅動程式檔案的位置。 |
| 在 macOS 上安裝 ODBC 驅動程式 | 適用於 macOS 的 Homebrew tap 與 formula 步驟,包括如何安裝 18、17 或 13.1 版。 |
| 安裝 unixODBC 驅動管理器(Linux 和 macOS) | 安裝或升級 unixODBC,這個驅動管理器負責在 Linux 和 macOS 上載入 ODBC 驅動程式。 |
設定和連線
| 文章 | 說明 |
|---|---|
| DSN 與連接字串關鍵字與屬性 | 完整的 連接字串 關鍵字、DSN 條目與SQLSetConnectAttr屬性目錄,並附有各項可接受的值。 |
| 連線字串關鍵字與資料來源名稱(Linux 與 macOS) | 如何在 Linux 和 macOS 上odbc.iniodbcinst.ini定義 DSN,以及針對這些平台的傳輸層安全性(TLS)和傳輸控制協定(TCP)keep-alive 設定。 |
| ODBC 資料來源管理員 DSN(Windows) | 透過 Windows 使用者介面設定資料來源,而非使用 連接字串。 |
| 驅動程式感知連線池(Windows) | 哪些 連接字串 關鍵字和屬性會將連線放入獨立的池中,哪些則需要額外往返重置。 |
認證與安全
| 文章 | 說明 |
|---|---|
| 使用 Microsoft Entra ID 搭配 ODBC 驅動程式 | 每個 Authentication 關鍵字值,從管理身份、服務主體到互動式與整合式,並附有各自所需的設定。 |
| 搭配 ODBC 驅動程式使用 Always Encrypted | 在將敏感欄位值傳送到資料庫前,先加密這些值,並提供驅動程式的 API 摘要及記載的限制。 |
| 資料分類 | 閱讀伺服器附在機密欄位上的敏感性標籤,讓您的應用程式能執行自己的資料保護政策。 |
| 使用整合驗證(Linux 和 macOS) | 設定 Kerberos,讓 Linux 或 macOS 用戶端能用 Windows 憑證連線,而非 SQL Server 登入。 |
高可用性與韌性
| 文章 | 說明 |
|---|---|
| 連線韌性 |
ConnectRetryCount 和 ConnectRetryInterval 如何在伺服器於連線閒置時中斷連線後恢復連線,以及在無法恢復時驅動程式會傳回的 IMCxx 錯誤。 |
| 高可用性與災難復原 | 透過可用性群組監聽器連接,並使用 MultiSubnetFailover 故障轉移,避免在子網路逾時卡頓。 |
| 使用透明網路 IP 解析 | 舊有 TransparentNetworkIPResolution 備援如何排序跨多個網際網路協定(IP)位址的連線嘗試,以及為什麼 MultiSubnetFailover 會取代它。 |
處理資料
| 文章 | 說明 |
|---|---|
| Vector 資料類型 | 綁定、傳送及擷取 vector 型別,包括其原生 C 表示法及大量複製支援。 |
| 用 XA 交易來處理 DTC | 透過 Windows、Linux 或 macOS 上的 Microsoft Distributed Transaction Coordinator,將 SQL Server 登錄到分散式交易中。 |
| 程式設計指引(Linux 與 macOS) | 查找 Linux 與 macOS 的程式設計指引,包括功能狀態、字元集考量、OpenSSL 及 Alpine Linux。 |
診斷和疑難解答
| 文章 | 說明 |
|---|---|
| 連線加密故障排除 | 修正版本 18 因預設加密而出現的憑證與加密錯誤。 |
| 資料存取追蹤(Linux 與 macOS) | 開啟驅動追蹤,並在需要查看應用程式實際呼叫時擷取日誌檔。 |
| 已知問題(Linux 與 macOS) | 已確認缺陷及其解決方法。 在提出支援案件前,請先確認這裡。 |
| 常見問題(Linux 與 macOS) | 針對 Linux 和 macOS 上驅動程式最常被問到的問題,提供簡短回答。 |
發行說明與錯誤修正
| 文章 | 說明 |
|---|---|
| Windows 版本的發行說明 | 每個 Windows 驅動版本中的新功能、行為變更與修正。 |
| Linux 與 macOS 版本的發行說明 | 每個 Linux 和 macOS 驅動版本中都有新功能、行為變更與修正。 |
| SQL Server 工具的發行說明 | 修改了 sqlcmd 和 bcp 工具,這些工具在 Linux 和 macOS 上是獨立安裝於驅動程式之外的。 |
Reference
| 文章 | 說明 |
|---|---|
| Windows 上的 ODBC 驅動程式 | 針對驅動程式在 Windows 上支援的版本逐版本摘要,以及 Windows 專屬文章的索引。 |
| Windows 上 ODBC 驅動程式的功能 | 哪個版本引入了每個 Windows 功能,以及隨之而來的行為變更。 |
提出功能要求
若要請求功能,請透過 SQL Server 回饋提交想法。
相關內容
- ODBC 程式設計師參考:本驅動程式實作的 ODBC API 規範,與驅動程式分開文件。
- SQL Server 原生客戶端功能:歷史 API 上下文。 請查閱目前驅動程式文件,了解平台支援、驗證和加密行為。 原生客戶端和 OLE DB 的指引並未說明目前 ODBC 驅動程式的支援情況。
- BCP 工具:批量複製工具,與驅動程式分開安裝。
- SQLcmd 工具:命令列查詢工具,與驅動程式分開安裝。
- 驅動程式功能支援矩陣
- SQL Server 驅動程式部落格