1. 程式人生 > >ODBC:如何在執行SQL插入操作時返回主鍵列的自增值和其它列的預設生成值

ODBC:如何在執行SQL插入操作時返回主鍵列的自增值和其它列的預設生成值

        執行SQL插入操作時,如果能將主鍵列的自增值和其它列的預設生成值一同返回,將極大的提高SQL的執行效率。我們知道,如果是編寫儲存過程的話,是可以解決這個問題的。但編寫儲存過程的問題是需要單獨編寫並在資料庫中建立,如果表比較多的話不太好維護。那是否可以不用儲存過程,只用一條SQL語句就可以解決這個問題呢?下面我們使用MS SQL資料庫和ODBC驅動看怎麼解決這個問題。

假設有如下表:

CREATE TABLE tb_test(
    id          bigint      IDENTITY(1,1) PRIMARY KEY,   --id,自增主鍵
    name        varchar(50) NOT NULL,                    --名字
    crt_dtm     datetime    DEFAULT (sysdatetime()),     --建立時間,插入時會賦當前預設時間
    opt_dtm     datetime    NULL,                        --操作時間
    size        int         NOT NULL                     --大小
)

 其中,id是自增主鍵列,crt_dtm在使用者未輸入時提供當前時間作為預設值。一般會執行的SQL語句如下:

insert into tb_test (name, opt_dtm, size) values (?,?,?)

但這條語句本身不會返回id和crt_dtm的值,所以我們要藉助MS SQL的表變數和output語句將這兩個值取出。

declare @t table (id bigint, crt_dtm datetime); 
INSERT INTO tb_test(name, opt_dtm, size) OUTPUT INSERTED.id, INSERTED.crt_dtm INTO @t VALUES (?, ?, ?); 
SELECT id, crt_dtm FROM @t;

首先,建立表變數t,其中定義了id和crt_dtm兩個欄位,用於將insert後生成的值存放到這兩個欄位中;

然後,在insert語句中使用OUTPUT語句,將insert後生成的id和crt_dtm的值儲存到表變數t對應的列中;

最後,查詢表變數t,將id和crt_dtm兩個欄位的值返回。

下面來看看如何使用ODBC來執行此SQL語句:

void execSql(SQLHSTMT hstmt, SQLCHAR* name, SQL_TIMESTAMP_STRUCT optDtm, SQLUINTEGER size) {
  SQLRETURN retCode;
  
  SQLLEN cbSize = 0, cbOptDtm = 0, cbName = SQL_NTS;
  
  retCode = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 100, 0, (SQLPOINTER)name, 0, &cbName);
  retCode = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_TYPE_TIMESTAMP, SQL_TYPE_TIMESTAMP, 30, 0, &optDtm, 0, &cbOptDtm);
  retCode = SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_BIGINT, 20, 0, &size, 0, &cbSize);
  
  SQLWCHAR* sql = (SQLWCHAR*)L"declare @t table (id bigint, crt_dtm datetime2); INSERT INTO tb_test(name, opt_dtm, size) OUTPUT INSERTED.id, INSERTED.crt_dtm INTO @t VALUES (?, ?, ?); SELECT id, crt_dtm FROM @t;";
  
  retCode = SQLPrepare(hstmt, sql, SQL_NTS);
  retCode = SQLExecute(hstmt);
  
  long id = 0;
  SQL_TIMESTAMP_STRUCT crtDtm;
  SQLLEN cbId = 0, cbCrtDtm = 0;
  
  retCode = SQLBindCol(hstmt, 1, SQL_C_SLONG, &id, 8, &cbId);
  retCode = SQLBindCol(hstmt, 2, SQL_C_TYPE_TIMESTAMP, &crtDtm, sizeof(crtDtm), &cbCrtDtm);
  
  retCode = SQLMoreResults(hstmt);  // (1)
  
  retCode = SQLFetch(hstmt);
  
  std::stringstream ss;
  ss << crtDtm.year << "-" << crtDtm.month << "-" << crtDtm.day << " " << crtDtm.hour << ":" << crtDtm.minute << ":" << crtDtm.second << "." << crtDtm.fraction;
  
  std::cout << std::to_string(id) << ":" << ss.str() << std::endl;
}

此函式執行完成後就可以把最新的id和crtDtm的值取出。

      這段程式碼中最關鍵的是(1)這行程式碼,由於sql中包含一個insert和一個select,所以返回結果是兩個結果集,如果要取到select的返回結果集,就需要呼叫SQLMoreResults移動到下一個結果集。

參考文件