Quickstart: Verbinden und Abfrage mit C++ und ODBC

Gilt für:SQL ServerAzure SQL-Datenbank

In diesem Quickstart baust du eine C++-Konsolenanwendung auf Windows, Linux oder macOS. Die Anwendung verbindet sich mit einer AdventureWorksLT Datenbank, indem sie den Microsoft ODBC Driver 18 für SQL Server verwendet, bindet einen Abfrageparameter, führt die Abfrage aus und liest die resultierenden Zeilen.

Voraussetzungen

  • Microsoft ODBC Driver 18 für SQL Server. Installiere den Treiber für Windows, Linux oder macOS.
  • Ein C++17-Compiler und die ODBC-Entwicklungsdateien der Plattform:
    • Unter Windows installieren Sie Visual Studio 2022 oder die Build Tools für Visual Studio 2022 mit der Desktop-Entwicklung mit C++ Workload. Das Windows SDK liefert die ODBC-Header und odbc32.lib.
    • Unter Linux installiere einen C++-Compiler und das unixODBC-Entwicklungspaket für deine Distribution. Das Paket liefert die ODBC-Header und libodbc.
    • Installiere auf macOS die Xcode-Kommandozeilen-Tools und unixODBC aus Homebrew.
  • Eine Datenbank in Azure SQL-Datenbank oder SQL Server, die die AdventureWorksLT Beispieldaten enthält. Für die Azure SQL-Datenbank wählen Sie die Beispiel-Datenquelle aus, wenn Sie eine einzelne Datenbank erstellen. Für SQL Server sollten Sie ein AdventureWorksLT Backup aus AdventureWorks-Beispieldatenbanken wiederherstellen.

Überprüfen Sie den Fahrer

Bestätigen Sie, dass der Treibermanager den Microsoft ODBC Driver 18 für SQL Server finden kann.

Führe diesen Befehl in PowerShell aus:

Get-OdbcDriver -Name "ODBC Driver 18 for SQL Server"

Jeder Befehl sollte ODBC Driver 18 for SQL Server angeben. Wenn der Treiber nicht aufgeführt ist, installiere ihn neu, bevor du weitermachst.

Konfigurieren Sie die Verbindung

Die Anwendung liest den vollständigen Verbindungszeichenfolge aus der ODBC_CONNECTION_STRING Umgebungsvariable. Sie zeigt den Verbindungszeichenfolge nicht an und akzeptiert ihn nicht als Kommandozeilenargument.

Verwenden Sie eine Driver-18-Verbindungszeichenfolge, die für Ihre Datenbank und Ihre Authentifizierungsmethode gültig ist. Das folgende Beispiel zur SQL-Authentifizierung funktioniert unter Windows, Linux und macOS, wenn die Datenbank SQL-Authentifizierung erlaubt:

Driver={ODBC Driver 18 for SQL Server};Server=tcp:<server>,1433;Database=<database>;UID=<user_id>;PWD=<password>;Encrypt=yes;TrustServerCertificate=no;

Für Microsoft Entra-Authentifizierungsoptionen siehe Verwenden Sie Microsoft Entra ID mit dem ODBC-Treiber. Für alle unterstützten Einstellungen siehe DSN- und Verbindungszeichenfolge-Schlüsselwörter und -attribute.

Microsoft ODBC Driver 18 aktiviert standardmäßig die Verschlüsselung. Geben Sie an, Encrypt=yes damit die Anforderungen der Anwendung explizit sind. TrustServerCertificate=no im Produktivbetrieb beibehalten, damit der Treiber das Serverzertifikat überprüft. Das Zertifikat muss den Servernamen und die Kette mit einer Zertifizierungsstelle abstimmen, der der Client vertraut. Für Konfigurationshinweise siehe Zertifikatsvalidierungsfehler.

Caution

TrustServerCertificate=yes Überspringt die Zertifikatsvalidierung. Nutze es nur für isolierte lokale Entwicklung, während du ein Zertifikat konfigurierst, dem der Client vertraut. Verwenden Sie sie nicht in der Produktion.

Setze die Umgebungsvariable, ohne die Verbindungszeichenfolge zu deiner Shell-Historie hinzuzufügen.

Öffne Developer PowerShell für VS 2022. Führe diese Befehle aus und füge dann die Verbindungszeichenfolge am Prompt ein:

$secureConnectionString = Read-Host "ODBC connection string" -AsSecureString
$env:ODBC_CONNECTION_STRING = [System.Net.NetworkCredential]::new(
    "", $secureConnectionString).Password
$secureConnectionString = $null

Eine Umgebungsvariable hält die Verbindungszeichenfolge aus der Quelldatei heraus, aber der Prozess und seine Kindprozesse können sie lesen. Für Produktionsanwendungen verwenden Sie, wo möglich, die Microsoft Entra-Authentifizierung, und rufen Sie Geheimnisse zur Laufzeit aus einem sicheren Speicher ab.

Erstellen der Anwendung

  1. Erstellen Sie ein Projektverzeichnis und ändern Sie es:

    New-Item -ItemType Directory odbc-quickstart
    Set-Location odbc-quickstart
    

  1. Erstellen Sie eine Datei mit dem Namen odbc-quickstart.cpp und dem folgenden Code:

    #ifdef _WIN32
    #include <windows.h>
    #endif
    
    #include <sql.h>
    #include <sqlext.h>
    #include <sqltypes.h>
    
    #include <cstdlib>
    #include <iomanip>
    #include <iostream>
    #include <string>
    
    std::string ReadEnvironmentVariable(const char* name)
    {
    #ifdef _WIN32
        char* value = nullptr;
        std::size_t length = 0;
        if (_dupenv_s(&value, &length, name) != 0 || value == nullptr)
            return {};
    
        std::string result(value);
        std::free(value);
        return result;
    #else
        const char* value = std::getenv(name);
        return value == nullptr ? std::string{} : value;
    #endif
    }
    
    void PrintDiagnostics(SQLSMALLINT handleType, SQLHANDLE handle)
    {
        SQLCHAR state[6];
        SQLINTEGER nativeError;
        SQLCHAR message[SQL_MAX_MESSAGE_LENGTH];
        SQLSMALLINT messageLength;
    
        for (SQLSMALLINT record = 1;
             SQL_SUCCEEDED(SQLGetDiagRec(handleType, handle, record, state,
                                         &nativeError, message, sizeof(message),
                                         &messageLength));
             ++record)
        {
            std::cerr << '[' << state << "] (" << nativeError << ") "
                      << message << '\n';
        }
    }
    
    bool Succeeded(SQLRETURN result, SQLSMALLINT handleType, SQLHANDLE handle)
    {
        if (SQL_SUCCEEDED(result))
            return true;
    
        PrintDiagnostics(handleType, handle);
        return false;
    }
    
    struct OdbcHandles
    {
        SQLHENV environment = SQL_NULL_HENV;
        SQLHDBC connection = SQL_NULL_HDBC;
        SQLHSTMT statement = SQL_NULL_HSTMT;
    
        ~OdbcHandles()
        {
            if (statement != SQL_NULL_HSTMT)
                SQLFreeHandle(SQL_HANDLE_STMT, statement);
            if (connection != SQL_NULL_HDBC)
            {
                SQLDisconnect(connection);
                SQLFreeHandle(SQL_HANDLE_DBC, connection);
            }
            if (environment != SQL_NULL_HENV)
                SQLFreeHandle(SQL_HANDLE_ENV, environment);
        }
    };
    
    int main()
    {
        std::string connectionString =
            ReadEnvironmentVariable("ODBC_CONNECTION_STRING");
        if (connectionString.empty())
        {
            std::cerr << "Set ODBC_CONNECTION_STRING before running.\n";
            return 1;
        }
    
        OdbcHandles handles;
        SQLRETURN result = SQLAllocHandle(
            SQL_HANDLE_ENV, SQL_NULL_HANDLE, &handles.environment);
        if (!SQL_SUCCEEDED(result))
        {
            std::cerr << "Unable to allocate an ODBC environment handle.\n";
            return 1;
        }
    
        result = SQLSetEnvAttr(
            handles.environment,
            SQL_ATTR_ODBC_VERSION,
            reinterpret_cast<SQLPOINTER>(SQL_OV_ODBC3_80),
            0);
        if (!Succeeded(result, SQL_HANDLE_ENV, handles.environment))
            return 1;
    
        result = SQLAllocHandle(
            SQL_HANDLE_DBC, handles.environment, &handles.connection);
        if (!Succeeded(result, SQL_HANDLE_ENV, handles.environment))
            return 1;
    
        result = SQLDriverConnect(
            handles.connection,
            nullptr,
            reinterpret_cast<SQLCHAR*>(connectionString.data()),
            SQL_NTS,
            nullptr,
            0,
            nullptr,
            SQL_DRIVER_NOPROMPT);
        if (!Succeeded(result, SQL_HANDLE_DBC, handles.connection))
            return 1;
    
        result = SQLAllocHandle(
            SQL_HANDLE_STMT, handles.connection, &handles.statement);
        if (!Succeeded(result, SQL_HANDLE_DBC, handles.connection))
            return 1;
    
        SQLINTEGER minimumProductId = 0;
        SQLLEN minimumProductIdLength = 0;
        result = SQLBindParameter(
            handles.statement,
            1,
            SQL_PARAM_INPUT,
            SQL_C_SLONG,
            SQL_INTEGER,
            10,
            0,
            &minimumProductId,
            0,
            &minimumProductIdLength);
        if (!Succeeded(result, SQL_HANDLE_STMT, handles.statement))
            return 1;
    
        SQLCHAR query[] =
            "SELECT TOP (5) ProductID, Name "
            "FROM SalesLT.Product "
            "WHERE ProductID > ? "
            "ORDER BY ProductID;";
        result = SQLExecDirect(handles.statement, query, SQL_NTS);
        if (!Succeeded(result, SQL_HANDLE_STMT, handles.statement))
            return 1;
    
        std::cout << "Product ID  Name\n"
                  << "----------  ----\n";
    
        while (SQL_SUCCEEDED(result = SQLFetch(handles.statement)))
        {
            SQLINTEGER productId;
            SQLLEN productIdLength;
            SQLCHAR productName[256];
            SQLLEN productNameLength;
    
            result = SQLGetData(
                handles.statement, 1, SQL_C_SLONG, &productId,
                sizeof(productId), &productIdLength);
            if (!Succeeded(result, SQL_HANDLE_STMT, handles.statement))
                return 1;
    
            result = SQLGetData(
                handles.statement, 2, SQL_C_CHAR, productName,
                sizeof(productName), &productNameLength);
            if (!Succeeded(result, SQL_HANDLE_STMT, handles.statement))
                return 1;
    
            std::cout << std::left << std::setw(12) << productId
                      << productName << '\n';
        }
    
        if (result != SQL_NO_DATA)
        {
            PrintDiagnostics(SQL_HANDLE_STMT, handles.statement);
            return 1;
        }
    
        return 0;
    }
    

Die Anwendung verwendet nur die Standard-ODBC-API, daher enthält sie die Treiber-Manager-Header und Links zur Treiber-Manager-Bibliothek. Die Verbindungszeichenfolge wählt zur Laufzeit Microsoft ODBC Driver 18 für SQL Server aus.

SQL_DRIVER_NOPROMPT verhindert SQLDriverConnect , dass ein Konfigurationsdialog geöffnet wird. Ist der Verbindungszeichenfolge unvollständig, gibt der Anruf einen Fehler zurück und die Anwendung druckt jeden Diagnosedatensatz.

Die Abfrage bindet 0 als SQL Server int Parameter, liest die ersten fünf Produkte in SalesLT.Productund ruft jede Produkt-ID und jeden Namen mit SQLGetDataab.

Erstellen und Ausführen der Anwendung

  1. Kompiliere im selben Entwickler-PowerShell-Fenster die Anwendung:

    cl /std:c++17 /EHsc /W4 odbc-quickstart.cpp /link odbc32.lib
    
  2. Führen Sie die Anwendung aus:

    .\odbc-quickstart.exe
    

Die Standardausgabe der Anwendung lautet:

Product ID  Name
----------  ----
680         HL Road Frame - Black, 58
706         HL Road Frame - Red, 58
707         Sport-100 Helmet, Red
708         Sport-100 Helmet, Black
709         Mountain Bike Socks, M

Lösche die Verbindungszeichenfolge, wenn du fertig bist.

Remove-Item Env:\ODBC_CONNECTION_STRING