使用 SQLSetPos 更新行集中的行

SQLSetPos 的更新操作使数据源使用每个绑定列的应用程序缓冲区中的数据更新表的一个或多个选定行(除非长度/指示器缓冲区中的值为 SQL_COLUMN_IGNORE)。 未绑定的列不会更新。

若要使用 SQLSetPos 更新行,应用程序将执行以下操作:

  1. 将新的数据值放在行集缓冲区中。 有关如何使用 SQLSetPos 发送长数据的信息,请参阅长数据以及 SQLSetPos 和 SQLBulkOperations

  2. 根据需要设置每个列的长度/指示器缓冲区中的值。 这是绑定到字符串缓冲区的列数据或 SQL_NTS 的字节长度,绑定到二进制缓冲区的列数据的字节长度,以及要设置为 NULL 的任何列的 SQL_NULL_DATA。

  3. 将不更新的列的长度/指示器缓冲区中的值设置为 SQL_COLUMN_IGNORE。 尽管应用程序可以跳过此步骤并重新发送现有数据,但这样做效率低下,并且有可能将值发送到读取时被截断的数据源。

  4. 调用操作设置为 SQL_UPDATE 的 SQLSetPos,将 RowNumber 设置为要更新的行数。 如果 RowNumber 为 0,将更新行集中的所有行。

SQLSetPos 返回后,当前行将设置为更新的行。

更新行集的所有行(RowNumber 等于 0)时,应用程序可以通过将行操作数组(由 SQL_ATTR_ROW_OPERATION_PTR 语句属性指向)的相应元素设置为 SQL_ROW_IGNORE 来禁用某些行的更新。 行操作数组的大小和元素数对应于行状态数组(由 SQL_ATTR_ROW_STATUS_PTR 语句属性指向)。 若要仅更新结果集中已成功提取且未从行集中删除的行,应用程序将使用来自提取行集的函数的行状态数组作为 SQLSetPos 的行操作数组。

对于作为更新发送到数据源的每一行,应用程序缓冲区都应具有有效的行数据。 如果应用程序缓冲区已被提取操作填充,并且保持了行状态数组,则所有这些行位置处的值都不应为 SQL_ROW_DELETED、SQL_ROW_ERROR 或 SQL_ROW_NOROW。

例如,以下代码允许用户滚动浏览 Customers 表并更新、删除或添加新行。 它会在调用 SQLSetPos 更新或添加新行之前,将新数据放在行集缓冲区中。 在行集缓冲区的末尾分配一个额外的行,用于保存新行;这样可以防止在缓冲区中放置新行的数据时覆盖现有数据。

#define UPDATE_ROW   100  
#define DELETE_ROW   101  
#define ADD_ROW      102  
  
SQLUINTEGER    CustIDArray[11];  
SQLCHAR        NameArray[11][51], AddressArray[11][51],   
               PhoneArray[11][11];  
SQLINTEGER     CustIDIndArray[11], NameLenOrIndArray[11],   
               AddressLenOrIndArray[11],  
               PhoneLenOrIndArray[11];  
SQLUSMALLINT   RowStatusArray[10], Action, RowNum;  
SQLRETURN      rc;  
SQLHSTMT       hstmt;  
  
// Set the SQL_ATTR_ROW_BIND_TYPE statement attribute to use column-wise   
// binding. Declare the rowset size with the SQL_ATTR_ROW_ARRAY_SIZE   
// statement attribute. Set the SQL_ATTR_ROW_STATUS_PTR statement   
// attribute to point to the row status array.  
SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, SQL_CURSOR_KEYSET_DRIVEN, 0);  
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 0);  
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, 10, 0);  
SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR, RowStatusArray, 0);  
  
// Bind arrays to the CustID, Name, Address, and Phone columns.  
SQLBindCol(hstmt, 1, SQL_C_ULONG, CustIDArray, 0, CustIDIndArray);  
SQLBindCol(hstmt, 2, SQL_C_CHAR, NameArray, sizeof(NameArray[0]), NameLenOrIndArray);  
SQLBindCol(hstmt, 3, SQL_C_CHAR, AddressArray, sizeof(AddressArray[0]),  
            AddressLenOrIndArray);  
SQLBindCol(hstmt, 4, SQL_C_CHAR, PhoneArray, sizeof(PhoneArray[0]),  
            PhoneLenOrIndArray);  
  
// Execute a statement to retrieve rows from the Customers table.  
SQLExecDirect(hstmt, "SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);  
  
// Fetch and display the first 10 rows.  
rc = SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0);  
DisplayData(CustIDArray, CustIDIndArray, NameArray, NameLenOrIndArray, AddressArray,  
            AddressLenOrIndArray, PhoneArray, PhoneLenOrIndArray, RowStatusArray);  
  
// Call GetAction to get an action and a row number from the user.  
while (GetAction(&Action, &RowNum)) {  
   switch (Action) {  
  
      case SQL_FETCH_NEXT:  
      case SQL_FETCH_PRIOR:  
      case SQL_FETCH_FIRST:  
      case SQL_FETCH_LAST:  
      case SQL_FETCH_ABSOLUTE:  
      case SQL_FETCH_RELATIVE:  
         // Fetch and display the requested data.  
         SQLFetchScroll(hstmt, Action, RowNum);  
         DisplayData(CustIDArray, CustIDIndArray,  
                     NameArray, NameLenOrIndArray,  
                     AddressArray, AddressLenOrIndArray,  
                     PhoneArray, PhoneLenOrIndArray, RowStatusArray);  
         break;  
  
      case UPDATE_ROW:  
         // Place the new data in the rowset buffers and update the   
         // specified row.  
         GetNewData(&CustIDArray[RowNum - 1], &CustIDIndArray[RowNum - 1],  
                  NameArray[RowNum - 1], &NameLenOrIndArray[RowNum - 1],  
                  AddressArray[RowNum - 1], &AddressLenOrIndArray[RowNum - 1],  
                  PhoneArray[RowNum - 1], &PhoneLenOrIndArray[RowNum - 1]);  
         SQLSetPos(hstmt, RowNum, SQL_UPDATE, SQL_LOCK_NO_CHANGE);  
         break;  
  
      case DELETE_ROW:  
         // Delete the specified row.  
         SQLSetPos(hstmt, RowNum, SQL_DELETE, SQL_LOCK_NO_CHANGE);  
         break;  
  
      case ADD_ROW:  
         // Place the new data in the rowset buffers at index 10.   
         // This is an extra element for new rows so rowset data is   
         // not overwritten. Insert the new row. Row 11 corresponds   
         // to index 10.  
         GetNewData(&CustIDArray[10], &CustIDIndArray[10],  
                     NameArray[10], &NameLenOrIndArray[10],  
                     AddressArray[10], &AddressLenOrIndArray[10],  
                     PhoneArray[10], &PhoneLenOrIndArray[10]);  
         SQLSetPos(hstmt, 11, SQL_ADD, SQL_LOCK_NO_CHANGE);  
         break;  
   }  
}  
  
// Close the cursor.  
SQLCloseCursor(hstmt);