Verwenden von SQLBindCol

Die Anwendung bindet Spalten durch Aufrufen von SQLBindCol. Diese Funktion bindet jeweils eine Spalte. Damit gibt die Anwendung Folgendes an:

  • Die Spaltennummer. Spalte 0 ist die Textmarkenspalte; Diese Spalte ist in einigen Resultsets nicht enthalten. Alle anderen Spalten werden beginnend mit der Zahl 1 nummeriert. Es ist ein Fehler, eine spalte mit höherer Nummerierung zu binden, als spalten im Resultset vorhanden sind. Dieser Fehler kann erst erkannt werden, wenn das Resultset erstellt wurde, sodass er von SQLFetch und nicht von SQLBindCol zurückgegeben wird.

  • Der Datentyp C, die Adresse und die Bytelänge der Variablen, die an die Spalte gebunden sind. Fehler beim Angeben eines C-Datentyps, in den der SQL-Datentyp der Spalte nicht konvertiert werden kann; Dieser Fehler wird möglicherweise erst erkannt, wenn das Resultset erstellt wurde, sodass er von SQLFetch und nicht von SQLBindCol zurückgegeben wird. Eine Liste der unterstützten Konvertierungen finden Sie unter Konvertieren von Daten aus SQL in C-Datentypen in Anhang D: Datentypen. Informationen zur Bytelänge finden Sie unter "Datenpufferlänge".

  • Die Adresse eines Längen-/Indikatorpuffers. Der Längen-/Indikatorpuffer ist optional. Sie wird verwendet, um die Bytelänge von Binär- oder Zeichendaten zurückzugeben oder SQL_NULL_DATA zurückzugeben, wenn die Daten NULL sind. Weitere Informationen finden Sie unter Verwenden von Längen-/Indikatorwerten.

Wenn SQLBindCol aufgerufen wird, ordnet der Treiber diese Informationen der Anweisung zu. Wenn jede Datenzeile abgerufen wird, werden die Daten für jede Spalte in den gebundenen Anwendungsvariablen mithilfe der Informationen platziert.

Der folgende Code bindet beispielsweise Variablen an die Spalten "SalesPerson" und "CustID". Daten für die Spalten werden in "SalesPerson " und "CustID" zurückgegeben. Da SalesPerson ein Zeichenpuffer ist, gibt die Anwendung die Bytelänge (11) an, damit der Treiber bestimmen kann, ob die Daten abgeschnitten werden sollen. Die Bytelänge des zurückgegebenen Titels oder ob er NULL ist, wird in SalesPersonLenOrInd zurückgegeben.

Da CustID eine ganzzahlige Variable ist und eine feste Länge aufweist, muss die Bytelänge nicht angegeben werden. Der Treiber geht davon aus, dass es sizeof(SQLUINTEGER)ist. Die Bytelänge der zurückgegebenen Kunden-ID-Daten oder ob es NULL ist, wird in CustIDInd zurückgegeben. Beachten Sie, dass die Anwendung nur daran interessiert ist, ob das Gehalt NULL ist, da die Bytelänge immer sizeof(SQLUINTEGER)ist.

SQLCHAR       SalesPerson[11];  
SQLUINTEGER   CustID;  
SQLINTEGER    SalesPersonLenOrInd, CustIDInd;  
SQLRETURN     rc;  
SQLHSTMT      hstmt;  
  
// Bind SalesPerson to the SalesPerson column and CustID to the   
// CustID column.  
SQLBindCol(hstmt, 1, SQL_C_CHAR, SalesPerson, sizeof(SalesPerson),  
            &SalesPersonLenOrInd);  
SQLBindCol(hstmt, 2, SQL_C_ULONG, &CustID, 0, &CustIDInd);  
  
// Execute a statement to get the sales person/customer of all orders.  
SQLExecDirect(hstmt, "SELECT SalesPerson, CustID FROM Orders ORDER BY SalesPerson",  
               SQL_NTS);  
  
// Fetch and print the data. Print "NULL" if the data is NULL. Code to   
// check if rc equals SQL_ERROR or SQL_SUCCESS_WITH_INFO not shown.  
while ((rc = SQLFetch(hstmt)) != SQL_NO_DATA) {  
   if (SalesPersonLenOrInd == SQL_NULL_DATA)   
            printf("NULL                     ");  
   else   
            printf("%10s   ", SalesPerson);  
   if (CustIDInd == SQL_NULL_DATA)   
         printf("NULL\n");  
   else   
            printf("%d\n", CustID);  
}  
  
// Close the cursor.  
SQLCloseCursor(hstmt);  

Der folgende Code führt eine VOM Benutzer eingegebene SELECT-Anweisung aus und druckt jede Datenzeile im Resultset. Da die Anwendung die Form des von der SELECT-Anweisung erstellten Resultsets nicht vorhersehen kann, können hartcodierte Variablen nicht wie im vorherigen Beispiel an das Resultset gebunden werden. Stattdessen weist die Anwendung einen Puffer zu, der die Daten und einen Längen-/Indikatorpuffer für jede Spalte in dieser Zeile enthält. Für jede Spalte berechnet sie den Offset bis zum Anfang des Speichers für die Spalte und passt diesen Offset so an, dass die Daten- und Längen-/Indikatorpuffer für die Spalte bei Ausrichtungsgrenzen beginnen. Anschließend wird der Speicher an den Offset an die Spalte gebunden. Aus Der Sicht des Treibers ist die Adresse dieses Speichers nicht von der Adresse einer Variablen zu unterscheiden, die im vorherigen Beispiel gebunden ist. Weitere Informationen zur Ausrichtung finden Sie unter "Ausrichtung".

// This application allocates a buffer at run time. For each column, this   
// buffer contains memory for the column's data and length/indicator.   
// For example:  
//      column 1         column 2      column 3      column 4  
// <------------><---------------><-----><------------>  
//      db1   li1   db2   li2   db3   li3   db4   li4  
//      |      |      |      |      |      |      |         |  
//      _____V_____V________V_______V___V___V______V_____V_  
// |__________|__|_____________|__|___|__|__________|__|  
//  
// dbn = data buffer for column n  
// lin = length/indicator buffer for column n  
  
// Define a macro to increase the size of a buffer so that it is a   
// multiple of the alignment size. Thus, if a buffer starts on an   
// alignment boundary, it will end just before the next alignment   
// boundary. In this example, an alignment size of 4 is used because   
// this is the size of the largest data type used in the application's   
// buffer--the size of an SDWORD and of the largest default C data type   
// are both 4. If a larger data type (such as _int64) was used, it would   
// be necessary to align for that size.  
#define ALIGNSIZE 4  
#define ALIGNBUF(Length) Length % ALIGNSIZE ? \  
                  Length + ALIGNSIZE - (Length % ALIGNSIZE) : Length  
  
SQLCHAR        SelectStmt[100];  
SQLSMALLINT    NumCols, *CTypeArray, i;  
SQLINTEGER *   ColLenArray, *OffsetArray, SQLType, *DataPtr;  
SQLRETURN      rc;   
SQLHSTMT       hstmt;  
  
// Get a SELECT statement from the user and execute it.  
GetSelectStmt(SelectStmt, 100);  
SQLExecDirect(hstmt, SelectStmt, SQL_NTS);  
  
// Determine the number of result set columns. Allocate arrays to hold   
// the C type, byte length, and buffer offset to the data.  
SQLNumResultCols(hstmt, &NumCols);  
CTypeArray = (SQLSMALLINT *) malloc(NumCols * sizeof(SQLSMALLINT));  
ColLenArray = (SQLINTEGER *) malloc(NumCols * sizeof(SQLINTEGER));  
OffsetArray = (SQLINTEGER *) malloc(NumCols * sizeof(SQLINTEGER));  
  
OffsetArray[0] = 0;  
for (i = 0; i < NumCols; i++) {  
   // Determine the column's SQL type. GetDefaultCType contains a switch   
   // statement that returns the default C type for each SQL type.  
   SQLColAttribute(hstmt, ((SQLUSMALLINT) i) + 1, SQL_DESC_TYPE, NULL, 0, NULL, (SQLPOINTER) &SQLType);  
   CTypeArray[i] = GetDefaultCType(SQLType);  
  
   // Determine the column's byte length. Calculate the offset in the   
   // buffer to the data as the offset to the previous column, plus the   
   // byte length of the previous column, plus the byte length of the   
   // previous column's length/indicator buffer. Note that the byte   
   // length of the column and the length/indicator buffer are increased   
   // so that, assuming they start on an alignment boundary, they will  
   // end on the byte before the next alignment boundary. Although this   
   // might leave some holes in the buffer, it is a relatively   
   // inexpensive way to guarantee alignment.  
   SQLColAttribute(hstmt, ((SQLUSMALLINT) i)+1, SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &ColLenArray[i]);  
   ColLenArray[i] = ALIGNBUF(ColLenArray[i]);  
   if (i)  
      OffsetArray[i] = OffsetArray[i-1]+ColLenArray[i-1]+ALIGNBUF(sizeof(SQLINTEGER));  
}  
  
// Allocate the data buffer. The size of the buffer is equal to the   
// offset to the data buffer for the final column, plus the byte length   
// of the data buffer and length/indicator buffer for the last column.  
void *DataPtr = malloc(OffsetArray[NumCols - 1] +  
               ColLenArray[NumCols - 1] + ALIGNBUF(sizeof(SQLINTEGER)));  
  
// For each column, bind the address in the buffer at the start of the   
// memory allocated for that column's data and the address at the start   
// of the memory allocated for that column's length/indicator buffer.  
for (i = 0; i < NumCols; i++)  
   SQLBindCol(hstmt,  
            ((SQLUSMALLINT) i) + 1,  
            CTypeArray[i],  
            (SQLPOINTER)((SQLCHAR *)DataPtr + OffsetArray[i]),  
            ColLenArray[i],  
            (SQLINTEGER *)((SQLCHAR *)DataPtr + OffsetArray[i] + ColLenArray[i]));  
  
// Retrieve and print each row. PrintData accepts a pointer to the data,   
// its C type, and its byte length/indicator. It contains a switch   
// statement that casts and prints the data according to its type. Code   
// to check if rc equals SQL_ERROR or SQL_SUCCESS_WITH_INFO not shown.  
while ((rc = SQLFetch(hstmt)) != SQL_NO_DATA) {  
   for (i = 0; i < NumCols; i++) {  
      PrintData((SQLCHAR *)DataPtr[OffsetArray[i]], CTypeArray[i],  
               (SQLINTEGER *)((SQLCHAR *)DataPtr[OffsetArray[i] + ColLenArray[i]]));  
   }  
}  
  
// Close the cursor.  
SQLCloseCursor(hstmt);