Sterownik ODBC firmy Microsoft dla programu SQL Server

pobierz sterownika ODBC

ODBC to natywny interfejs programowania aplikacji (API) do dostępu do danych, przeznaczony dla aplikacji napisanych w językach C i C++. Sterownik Microsoft ODBC dla SQL Server łączy się z SQL Server, Azure SQL Database, Azure SQL Managed Instance, Azure Synapse Analytics oraz bazą danych SQL w Microsoft Fabric. Aby uzyskać wersje bazy danych, które obsługuje każda wersja sterownika, zobacz kompatybilność wersji SQL.

Twoja aplikacja wywołuje menedżera sterowników ODBC, który ładuje sterownik i przekazuje mu wywołania ODBC. Użyj menedżera sterowników ODBC w Windows w Windows oraz unixODBC na Linuksie i macOS.

Inne języki, które mogą używać ODBC, to COBOL, Perl, PHP i Python. ODBC jest szeroko stosowany w scenariuszach integracji danych, a sterowniki Microsoft for PHP for SQL Server są zbudowane na tym sterowniku.

Wariant ODBC narzędzia sqlcmd i narzędzie bcp używają tego sterownika, ale są instalowane osobno: pakiet mssql-tools18 w systemach Linux i macOS oraz narzędzia Microsoft Command Line Utilities w systemie Windows. Wariant sqlcmd w Go nie używa tego sterownika. Użyj sqlcmd do uruchamiania Transact-SQL (T-SQL) instrukcji, procedur systemowych i plików skryptów. Użyj bcp do masowego kopiowania danych między instancją SQL Server a plikiem danych, w obu kierunkach.

Wybieranie punktu początkowego

Plan bazowy produkcji dla Azure SQL

Wykorzystaj ten kompletny program C++17 jako punkt wyjścia dla aplikacji hostowanej w Azure. Łączy się, uruchamia SELECT 1, sprawdza wynik i zwalnia uchwyty ODBC. Zainstaluj Driver 18 oraz pliki programistyczne ODBC platformy przed jego skompilowaniem. Informacje o wymaganiach wstępnych znajdują się w artykule Tworzenie aplikacji w językach C i C++ ze sterownikiem ODBC.

Ustaw w środowisku aplikacji ODBC_CONNECTION_STRING jako pełny ciąg połączenia. Poniższy przykład wykorzystuje zarządzaną tożsamość przypisaną przez system. Host musi mieć włączoną tę tożsamość, a baza danych musi przyznać mu dostęp. Informacje o wymaganiach hosta, tożsamościach przypisanych przez użytkownika oraz lokalnym uwierzytelnianiu deweloperskim można znaleźć w artykule Używanie identyfikatora Microsoft Entra ID ze sterownikiem 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 wymaga punktu końcowego obsługującego Tabular Data Stream (TDS) 8.0. W przeciwnym razie użyj Encrypt=yes;TrustServerCertificate=no. Skonfiguruj certyfikat, któremu klient ufa i którego nazwa odpowiada nazwie serwera. Aby uzyskać informacje o konfiguracji i weryfikacji, zobacz Rozwiązywanie problemów z szyfrowaniem połączenia.

Trzymaj dane logowania z dala od plików źródłowych i historii powłoki. Polecenia ustawiające zmienną środowiskową można znaleźć w sekcji Konfigurowanie połączenia. W systemach Linux i macOS podaj jego wartość jako UTF-8. Zapisz następujący program jako 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;
    }
}

Program żąda 30-sekundowego limitu połączenia dla maksymalnie czterech prób połączenia oraz 10-sekundowego limitu zapytania. Między nieudanymi próbami przejściowego połączenia czeka 10, 20 i 40 sekund, każda z losowym opóźnieniem od 0 do 250 milisekund. Te limity nie nakładają sztywnego ogólnego terminu uwierzytelniania. Przykład klasyfikuje wybrane przejściowe błędy połączenia; dostosuj politykę do swojego obciążenia roboczego za pomocą katalogu błędów przejściowych Azure SQL. Nie próbuje ponownie poleceń aplikacji ani operacji zapisu.

ConnectRetryCount i ConnectRetryInterval konfigurują odporność połączeń dla kwalifikujących się połączeń, które przerywają się podczas bezczynności. Nie ponawiają początkowego połączenia ani nie odtwarzają ponownie poleceń aplikacji. Utrzymuj opóźnienia odzyskiwania po bezczynności krótsze niż czasowy limit zapytania. Przykład rejestruje wartości SQLSTATE i natywne numery błędów zamiast łańcuchów połączeń lub pełnych komunikatów diagnostycznych. Jeśli potrzebujesz tekstu wiadomości do diagnostyki, zapisz go w chronionym dzienniku diagnostycznym.

Zbuduj i uruchom wersję bazową

Używaj środowiska kompilatora, które odpowiada architekturze procesora Twojej aplikacji. Na Windows uruchom polecenia w Developer PowerShell for Visual Studio. W systemach Linux i macOS używaj powłoki w środowisku, w którym dostępne są kompilator i pliki deweloperskie unixODBC.

cl /nologo /std:c++17 /EHsc /W4 odbc-baseline.cpp odbc32.lib
.\odbc-baseline.exe

Udane pierwsze połączenie generuje następujący wynik. Mogą pojawić się także dodatkowe zapisy diagnostyczne.

event=connected attempt=1
event=ready result=1

Wyczyść ODBC_CONNECTION_STRING, gdy skończysz: używaj Remove-Item Env:\ODBC_CONNECTION_STRING w PowerShell lub unset ODBC_CONNECTION_STRING w Bash lub Zsh.

Dla nasłuchiwaczy grup dostępności i ustawień przełączania awaryjnego używaj Wysoka dostępność i odzyskiwanie po awarii.

Kluczowe funkcje

  • Cross-platform: To samo API na Windows, Linux i macOS.
  • Uwierzytelnianie Microsoft Entra ID: Zarządzana tożsamość, jednostka usługi i zintegrowane uwierzytelnianie, plus uwierzytelnianie interaktywne w systemie Windows. Dostępne metody zależą od konfiguracji hosta i bazy danych.
  • Ścisłe szyfrowanie: połączenia TDS 8.0 z pełną walidacją certyfikatów w wersjach 18 i nowszych.
  • Zawsze szyfrowane: szyfrowanie po stronie klienta dla wrażliwych kolumn, z wsparciem dla niestandardowych dostawców keystore'ów.
  • Odporność połączenia: Przejrzyste przywrócenie połączenia, które zerwało się podczas bezczynności.
  • Wysoka dostępność: Obsługa detektora grupy dostępności za pomocą MultiSubnetFailover.
  • Klasyfikacja danych: Metadane wrażliwości dla kolumn klasyfikowanych.
  • Typ danych wektorowych: Natywne wsparcie dla typu wektorowego .
  • Transakcje rozproszone: Wsparcie dla transakcji XA poprzez Microsoft Distributed Transaction Coordinator (MSDTC).
  • Narzędzia towarzyszące: sqlcmd i bcp, zainstalowane osobno.

Wprowadzenie

Artykuł Description
Pobierz sterownik ODBC dla programu SQL Server Instalatory i pakiety do pobrania dla każdej obsługiwanej wersji sterownika na wszystkich trzech platformach.
Rozwijanie aplikacji w C i C++ z użyciem sterownika ODBC Które nagłówki uwzględnić, w jakiej kolejności, które biblioteki połączyć oraz jak wybierać między wykonywaniem asynchronicznym a wątkami.
Połącz się i zapytuj bazę danych za pomocą C++ Kompletny przykład w C++, który nawiązuje połączenie, uruchamia zapytanie i odczytuje wyniki, dzięki czemu możesz zweryfikować konfigurację od początku do końca.
Cykl życia wsparcia Które wersje sterowników są nadal wspierane oraz kiedy każda z nich zakończy wsparcie.
Główne różnice między wersjami Co się psuje, gdy przechodzisz z wersji 17 na wersję 18, zaczynając od zmiany domyślnego szyfrowania?

Instalowanie sterownika

Artykuł Description
Wymagania systemowe, instalacja i pliki sterowników (Windows) Obsługiwane wersje na Windows, wiersz poleceń instalatora do cichego wdrożenia oraz miejsce, gdzie każdy plik sterownika ląduje na dysku.
Wymagania systemowe (Linux i macOS) Które dystrybucje systemu Linux i wydania macOS obsługuje każda wersja sterownika, a także zgodność z wersjami SQL Server.
Zainstaluj sterownik ODBC na Linuksie Kroki menedżera pakietów dla Alpine, Debian, Red Hat, SUSE, Ubuntu i Azure Linux, a także instalacja offline i lokalizacje plików sterowników.
Zainstaluj sterownik ODBC na macOS Instrukcje dotyczące repozytorium tap i formuły Homebrew dla systemu macOS, w tym instalacji wersji 18, 17 lub 13.1.
Zainstaluj menedżera sterowników unixODBC (Linux i macOS) Zainstaluj lub zaktualizuj unixODBC, menedżera sterowników, który ładuje sterownik ODBC na Linuksie i macOS.

Konfigurowanie i łączenie

Artykuł Description
Słowa kluczowe i atrybuty DSN oraz ciągu połączenia Pełny katalog słów kluczowych ciągów połączenia, wpisów DSN i atrybutów SQLSetConnectAttr, wraz z akceptowanymi wartościami dla każdego z nich.
Słowa kluczowe w ciągu połączeń oraz nazwy źródeł danych (Linux i macOS) Jak odbc.ini i odbcinst.ini definiują DSN na Linuksie i macOS, a także ustawienia Transport Layer Security (TLS) i Transmission Control Protocol (TCP) keep-alive specyficzne dla tych platform.
Administrator źródeł danych ODBC DSN (Windows) Skonfiguruj źródło danych przez interfejs użytkownika systemu Windows zamiast parametrów połączenia.
Pula połączeń z uwzględnieniem sterowników (Windows) Które słowa kluczowe i atrybuty parametrów połączenia powodują umieszczenie połączenia w osobnej puli, a które wymagają dodatkowego przebiegu komunikacji w celu zresetowania połączenia.

Uwierzytelnianie i zabezpieczenie

Artykuł Description
Użyj Microsoft Entra ID z sterownikiem ODBC Każda Authentication wartość słowa kluczowego — od zarządzanej tożsamości i nazwy głównej usługi po interaktywną i zintegrowaną — wraz z konfiguracją wymaganą dla każdej z nich.
Użyj Always Encrypted z sterownikiem ODBC Szyfrowanie czułych wartości kolumn przed wysłaniem ich do bazy danych — podsumowanie interfejsu API sterownika i udokumentowane ograniczenia.
Klasyfikacja danych Przeczytaj etykiety wrażliwości, które serwer przypisuje do kolumn klasyfikowanych, aby Twoja aplikacja mogła egzekwować własną politykę ochrony danych.
Korzystaj z uwierzytelniania zintegrowanego (Linux i macOS) Skonfiguruj Kerberos tak, aby klient Linux lub macOS mógł łączyć się z datami logowania Windows zamiast logowania do SQL Server.

Wysoka dostępność i odporność

Artykuł Description
Odporność połączenia Jak ConnectRetryCount i ConnectRetryInterval przywracają połączenie, gdy serwer zrywa je podczas bezczynności, oraz jakie błędy IMCxx zwraca sterownik, gdy przywrócenie połączenia nie jest możliwe.
Wysoka dostępność i odzyskiwanie po awarii Łącz się za pośrednictwem detektora grupy dostępności i użyj elementu MultiSubnetFailover, aby przełączanie awaryjne nie blokowało się z powodu limitu czasu podsieci.
Używaj przezroczystej rozdzielczości IP sieci Jak starszy TransparentNetworkIPResolution mechanizm rezerwowy porządkuje próby połączenia przez wiele adresów protokołu internetowego (IP) i dlaczego MultiSubnetFailover go zastępuje.

Praca z danymi

Artykuł Description
wektorowy typ danych Powiąż, wyślij i pobierz typ vector, w tym jego natywną reprezentację w C oraz obsługę kopiowania zbiorczego.
Korzystaj z transakcji XA z DTC Zarejestruj SQL Server w rozproszonej transakcji za pośrednictwem Microsoft Distributed Transaction Coordinator na Windows, Linux lub macOS.
Wytyczne programistyczne (Linux i macOS) Znajdź wskazówki dotyczące programowania na Linuksa i macOS, w tym status funkcji, rozważania dotyczące zestawów znaków, OpenSSL i Alpine Linux.

Diagnozowanie i rozwiązywanie problemów

Artykuł Description
Rozwiązywanie problemów z szyfrowaniem połączenia Napraw błędy certyfikatów i szyfrowania powodowane przez wersję 18, ponieważ domyślnie włącza ona szyfrowanie.
Śledzenie dostępu do danych (Linux i macOS) Włącz śledzenie sterowników i przechwyć plik logu, gdy musisz zobaczyć wywołania, które faktycznie wykonuje twoja aplikacja.
Znane problemy (Linux i macOS) Potwierdzone wady i ich obejścia. Sprawdź tutaj, zanim złożysz wniosek o wsparcie społeczne.
Najczęściej zadawane pytania (Linux i macOS) Krótkie odpowiedzi na najczęściej pojawiające się pytania dotyczące sterownika na Linuksie i macOS.

Informacje o wydaniu i poprawki błędów

Artykuł Description
Notatki do wydania dla Windows Nowe funkcje, zmiany zachowań i poprawki w każdej wersji sterownika Windows.
Informacje o wydaniu dla systemów Linux i macOS Nowe funkcje, zmiany zachowań i poprawki w każdej wersji sterowników Linux i macOS.
Notatki do wydania narzędzi SQL Server Zmiany w narzędziach sqlcmd i bcp , które instalują się oddzielnie od sterownika na Linuksie i macOS.

Reference

Artykuł Description
Sterownik ODBC na Windows Podsumowanie wersji po wersjach tego, co sterownik obsługuje na Windows, oraz indeks artykułów specyficznych dla Windows.
Funkcje sterownika ODBC na Windows Która wersja wprowadziła każdą funkcję Windows oraz zmiany w zachowaniu, które się z nią wiązały.

Prośba o funkcję

Aby zgłosić prośbę o funkcję, złóż pomysł przez SQL Server feedback.