ODBC drivers >

JDBC drivers >

Bridges, gateways >

Other >

All products

Connect JDBC to any ODBC driver

Connect JDBC to Microsoft Access

Cross-platform or cross-architecture access to any ODBC driver

ODBC access to any JDBC driver

Connect dbExpress applications to any ODBC driver

Connect XML applications to any ODBC driver

Access mutiple ODBC data sources from one SQL statement

Create custom ODBC drivers

Bespoke data access requirements

In the pipeline

Support

Resources

Quick start

Licensing

Knowledge Base

User Guides

Company

About Us

Careers & Partners

Download ODBC Drivers for

Oracle, SQL Server, Salesforce, MongoDB, Access, Derby, InterBase, DB2, & more.

Learn More
/**********************************************************************
* FILENAME : TVPCopyTable.c
*
* DESCRIPTION :
* This example copies one table to another and illustrates
* the use of a Table-Valued Parameter to pass several records
* as one parameter to a stored procedure to perform the insert.
*
* Records are read from the source table and saved into an
* array until a batch of records is assembled. The array in
* which the batch of records is saved is also used as a
* Table-Valued Parameter for the insert. It is suggested,
* table-valued parameters perform well for inserting upto
* 1000 rows but can only be used as READONLY input and DML
* operations such as INSERT, DELETE and UPDATE cannot be
* performed on them.
*
* The source and destination tables both have an identity field.
* To maintain consistency during the copy, the stored procedure
* performing the insert issues a SET IDENTITY_INSERT ON prior to
* the insert and a SET IDENTITY_INSERT OFF afterwards. This
* enables write access to the identity field and stops SQL
* Server from generating one automatically. This then allows the
* identity column values from the source records to be copied
* into the destination records and in so doing make an identical
* copy.
*
* ODBC USAGE :
* The TVP used here represents a table type with 5 columns:
* PersonID, FirstName, LastName, Address and City.
*
* Prompts user for batch size
*
* Sets up the test by dropping and re-creating:-
* 1. Database Table (TestTVPTable2) - into which rows
* are written from rows in a TVP.
* 2. Stored Procedure (InsertFromTVP) - to select records
* from the TVP and insert them into TestTVPTable1.
* 3. TVP Table Variable (PersonTVP) - Table variable to
* hold records and passed as a single parameter to
* InsertFromTVP().
*
* Bind Parameters:
* Binds parameter 1 as a TVP
*
* Bind TVP Parameters:
* Sets focus on parameter 1
* Bind arrays to each of the 5 TVP columns and lengths. i.e.
* for PersonID, FirstName, LastName, Address and City
* Clears focus by setting focus to zero
*
* Prompts user for batch size.
* Loops until all records retrieved
* gets next record batch from source table using nextBatch()
* write batch via TVP (i.e. all records via 1 table type
* parameter)
*/
#include <stdio.h>
#include <stdlib.h>
#include <sql.h>
#include <sqlext.h>
#include <sqlncli.h>
#include <string.h>
#include "util.c"
#define LASTNAME_LEN 255
#define FIRSTNAME_LEN 255
#define ADDRESS_LEN 255
#define CITY_LEN 255
#define MAX_ARRAY_SIZE 20
#define MAX_BATCH_SIZE 5
// Row 0 across the arrays is used to store each record as it is
// fetched from the table. It is then moved into the part of the
// array which forms the Table-Valued Parameter (i.e. row 1 onwards);
SQLUINTEGER uiPersonID [MAX_ARRAY_SIZE+1];
SQLCHAR strFirstName [MAX_ARRAY_SIZE+1][FIRSTNAME_LEN];
SQLCHAR strLastName[MAX_ARRAY_SIZE+1][LASTNAME_LEN];
SQLCHAR strAddress[MAX_ARRAY_SIZE+1][ADDRESS_LEN];
SQLCHAR strCity[MAX_ARRAY_SIZE+1][CITY_LEN];
//
// Arrays of column values lengths. Same rule for row 0
//
SQLLEN lPersonID[MAX_ARRAY_SIZE+1];
SQLLEN lFirstName[MAX_ARRAY_SIZE+1];
SQLLEN lLastName[MAX_ARRAY_SIZE+1];
SQLLEN lAddress[MAX_ARRAY_SIZE+1];
SQLLEN lCity[MAX_ARRAY_SIZE+1];
SQLHSTMT hstmtSrc = SQL_NULL_HSTMT; 	// Statement handle for source table
//
// SetupTVPTest is used to create and/or drop the stored procedure,
// TVP table type and DB Table used in the test.
//
SQLRETURN SetupTVPTest (SQLHDBC hdbc, char createEm, char dropEm) {
 SQLHSTMT hstmt = SQL_NULL_HSTMT; 	// Statement handle
 SQLRETURN retcode = SQL_SUCCESS;	// Return status
 // Drop statements
 // The stored procedure takes one parameter, the persons
 // Table-Value Parameter Table.
 // DROP PROCEDURE
 SQLCHAR strDropTVPProc [] =
 "IF EXISTS (SELECT * FROM dbo.sysobjects WHERE "
 "id = object_id(N'dbo.InsertFromTVP') AND "
 "OBJECTPROPERTY(id, N'IsProcedure') = 1) "
 "DROP PROCEDURE dbo.InsertFromTVP";
 // CREATE PROCEDURE
 // Note: 1. READONLY is mandatory
 // 2. Use of SET IDENTITY_INSERT allows identity
 // column to have value inserted rather than
 // autogenerated, making exact copy possible.
	SQLCHAR strCreateTVPProc [] =
 "CREATE PROCEDURE InsertFromTVP (@Persons PersonTVP READONLY) "
 "AS "
 "SET IDENTITY_INSERT TestTVPTable2 ON;"
 "INSERT INTO TestTVPTable2 "
 "(PersonID, FirstName, LastName, Address, City) "
 "SELECT vPersonID, vFirstName, vLastName, vAddress, vCity "
 "FROM @Persons;"
 "SET IDENTITY_INSERT TestTVPTable2 OFF";
 // The Table-Valued Parameter PersonTVP
 // DROP TVP
 SQLCHAR strDropTVPType[] =
 "IF EXISTS (SELECT * FROM sys.types WHERE "
 "is_table_type = 1 AND name = 'PersonTVP') "
 "DROP TYPE dbo.PersonTVP";
 // CREATE TVP
 SQLCHAR strCreateTVPType [] =
 "CREATE TYPE PersonTVP AS TABLE ( "
 "vPersonID int,"
 "vFirstName varchar(255),"
 "vLastName varchar(255),"
 "vAddress varchar(255),"
 "vCity varchar(255)"
 ")";
 // The physical database table into which records are copied
 // DROP TABLE
 SQLCHAR strDropTVPTable [] =
 "IF EXISTS (SELECT * FROM dbo.sysobjects WHERE "
 "id = object_id(N'dbo.TestTVPTable2') AND "
 "OBJECTPROPERTY(id, N'IsUserTable') = 1) "
 "DROP TABLE dbo.TestTVPTable2";
 // CREATE TABLE
 SQLCHAR strCreateTVPTable [] =
 "CREATE TABLE TestTVPTable2 ("
 "PersonID int NOT NULL IDENTITY(1,1),"
 "FirstName varchar(255),"
 "LastName varchar(255),"
 "Address varchar(255),"
 "City varchar(255)"
 ")";
 // Allocate a statement handle
 retcode=SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
 CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_STMT)",
 hstmt, SQL_HANDLE_STMT);
 // DROP sequence is : Procedure, Table Type Parameter, Table
 if (dropEm=='Y') {
 	// Execute Drop Procedure
 	retcode = SQLExecDirect (hstmt, strDropTVPProc, SQL_NTS);
 CHECK_ERROR(retcode, "SQLExecDirect(DROPPROC)",
 hstmt, SQL_HANDLE_STMT);
 	// Execute Drop Type
 	retcode = SQLExecDirect (hstmt, strDropTVPType, SQL_NTS);
 CHECK_ERROR(retcode, "SQLExecDirect(DROPTYPE)",
 hstmt, SQL_HANDLE_STMT);
 	// Execute DROP Table
 	retcode = SQLExecDirect (hstmt, strDropTVPTable, SQL_NTS);
 CHECK_ERROR(retcode, "SQLExecDirect(DROPTABLE)",
 hstmt, SQL_HANDLE_STMT);
 }
 // CREATE sequence is : Table, Table Type Parameter, Procedure
 if (createEm=='Y')
 // Execute Create Table
 retcode = SQLExecDirect (hstmt, strCreateTVPTable, SQL_NTS);
 CHECK_ERROR(retcode, "SQLExecDirect(CREATETABLE)",
 hstmt, SQL_HANDLE_STMT);
 // Execute Create Type
 retcode = SQLExecDirect (hstmt, strCreateTVPType, SQL_NTS);
 CHECK_ERROR(retcode, "SQLExecDirect(CREATETYPE)",
 hstmt, SQL_HANDLE_STMT);
 // Execute Create Procedure
 retcode = SQLExecDirect (hstmt, strCreateTVPProc, SQL_NTS);
 CHECK_ERROR(retcode, "SQLExecDirect(CREATEPROC)",
 hstmt, SQL_HANDLE_STMT);
exit:
 // Free statement handle
 if (hstmt != SQL_NULL_HSTMT)
 SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
 return retcode;
}
// Assume src table is TestTBL1, i.e with same layout as TestTDB2
int nextBatch (SQLHDBC hdbc, int lastID, int batchSize) {
 SQLRETURN retcode=SQL_SUCCESS;
 SQLLEN indicator, rowCount;
 SQLSMALLINT columns;
 int currRec=0;
 //
 // Use keyword TOP to restrict number of rows to the batch size.
 // (differs from DB to DB. SQL SERVER/MSAccess uses TOP, MySQL
 // uses LIMIT and ORACLE uses ROWNUM). Order by PersonID is
 // important so each batch reads from where thw last one finished.
 //
 char *stmtStr="SELECT TOP %i * FROM TestTBL1 WHERE PersonID > %i "
 "ORDER BY PersonID";
 char stmt[128];
 // First call?
 if (hstmtSrc==SQL_NULL_HSTMT) {
 lastID=0;
 // Allocate a statement handle for selecting
 // records from the source table TestTBL1
 retcode=SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmtSrc);
 CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_STMT)",
 hstmtSrc, SQL_HANDLE_STMT);
 }
 // Row 0 is used for records being fetched from the source table
 // Row 1 upwards is where they are moved and where the TVP is
 // mapped to.
 retcode = SQLBindCol(hstmtSrc, 1, SQL_C_LONG, &uiPersonID[0],
 0, &lPersonID[0]);
 retcode = SQLBindCol(hstmtSrc, 2, SQL_C_CHAR, strFirstName[0],
 FIRSTNAME_LEN, &lFirstName[0]);
 retcode = SQLBindCol(hstmtSrc, 3, SQL_C_CHAR, strLastName[0],
 LASTNAME_LEN, &lLastName[0]);
 retcode = SQLBindCol(hstmtSrc, 4, SQL_C_CHAR, strAddress[0],
 ADDRESS_LEN, &lAddress[0]);
 retcode = SQLBindCol(hstmtSrc, 5, SQL_C_CHAR, strCity[0],
 CITY_LEN, &lCity[0]);
 sprintf (stmt, stmtStr, batchSize, lastID);
 retcode = SQLExecDirect (hstmtSrc, stmt, SQL_NTS);
 CHECK_ERROR(retcode, "SQLExecDirect(SELECT TOP)",
 hstmtSrc, SQL_HANDLE_STMT);
 do {
 // SQLNumResultCols() returns number of columns in result set.
 // if non zero use SQLFetch until SQL_NO_DATA returned
 retcode=SQLNumResultCols(hstmtSrc, &columns);
 	CHECK_ERROR(retcode, "SQLNumResultCols()",
 hstmtSrc, SQL_HANDLE_STMT);
 if (columns > 0) {
 while (SQLFetch(hstmtSrc) != SQL_NO_DATA) {
 currRec++;
 uiPersonID[currRec]=uiPersonID[0];
 strcpy (strFirstName[currRec], strFirstName[0]);
 strcpy (strLastName[currRec], strLastName[0]);
 strcpy (strAddress[currRec], strAddress[0]);
 strcpy (strCity[currRec], strCity[0]);
 }
 }
 } while (SQLMoreResults(hstmtSrc) == SQL_SUCCESS);
exit:
 return currRec;
}
int main () {
 SQLHENV henv = SQL_NULL_HENV; // Environment
 SQLHDBC hdbc = SQL_NULL_HDBC; // Connection handle
 SQLHSTMT hstmtDst = SQL_NULL_HSTMT; // Statement handle for dest table
 SQLRETURN retcode;			// Return status
 //
 // batchSize is the number of rows stored in the TVP
 // lastPersonID is a source record identity value and is used as the
 // starting point for each batch by selecting records with identity
 // greater than the last one we had. Works if select done uses ORDER BY
 // identity field.
 //
 int i, batchSize, rowsFound, lastPersonID=0;
 // The name of the TVP table data type
 SQLCHAR *TVPTableName = (SQLCHAR *) "PersonTVP";
 SQLLEN lTVPRowsUsed;
 // Get number of records to read from
 getInt ("Batch Size ", &batchSize, 'N', 0);
 if (batchSize==0) goto exit;
 if (batchSize>MAX_ARRAY_SIZE) batchSize=MAX_ARRAY_SIZE;
 // Allocate environment
 retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
 CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_ENV)",
 henv, SQL_HANDLE_ENV);
 // Set ODBC Version
 retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,
 (SQLPOINTER*)SQL_OV_ODBC3, 0);
 CHECK_ERROR(retcode, "SQLSetEnvAttr(SQL_ATTR_ODBC_VERSION)",
 henv, SQL_HANDLE_ENV);
 // Allocate connection
 retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
 CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_DBC)",
 henv, SQL_HANDLE_DBC);
 // Set Login Timeout
 retcode = SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
 CHECK_ERROR(retcode, "SQLSetConnectAttr(SQL_LOGIN_TIMEOUT)",
 hdbc, SQL_HANDLE_DBC);
 // Connect to DSN
 retcode = SQLConnect(hdbc, (SQLCHAR*) "SQLSRV2014", SQL_NTS,
 (SQLCHAR*) NULL, 0, NULL, 0);
 CHECK_ERROR(retcode, "SQLConnect(DSN:SQLSRV2014;)",
 hdbc, SQL_HANDLE_DBC);
 // Allocate Statement Handle
 retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmtDst);
 CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_STMT)",
 hstmtDst, SQL_HANDLE_STMT);
 // Setup stored procedure, Table-Value Parameter data type and database
 // Table.
 // Second parameter indicates whether the above are to be created
 // Third parameter indicates whether the above are to be dropped
 retcode = SetupTVPTest (hdbc, 'Y', 'Y');
 CHECK_ERROR(retcode, "SetupTVPTest()", hdbc, SQL_HANDLE_DBC);
 //
 // 1st step is bind all parameters in normal way.
 // Here we have just one parameter, a Table-Valued Parameter (TVP)
 // whish requires some specific parameters.
 //
 retcode = SQLBindParameter(
 hstmtDst, // Statement handle
 1, // Parameter Number
 SQL_PARAM_INPUT, // Input/Output Type (always INPUT for TVP)
 SQL_C_DEFAULT, // C - Type (always this for a TVP)
 SQL_SS_TABLE, // SQL Type (always this for a TVP)
 MAX_ARRAY_SIZE, // For a TVP this is max rows we will use
 0, // For a TVP this is always 0
 TVPTableName, // For a TVP this is the type name of the
 // TVP, and also a token returned by
 // SQLParamData.
 SQL_NTS, // For a TVP this is the length of the type
 // name or SQL_NTS.
 &lTVPRowsUsed); // For a TVP this is the number of rows
 // actually available.
 //
 // Now we need to bind a separate set of parameters, specifically for the
 // TVP. To do we set the focus on each TVP in turn, bind data to it in the
 // same way as for rows in a table.
 //
 // First set focus on the TVP, here it is param 1.
 retcode = SQLSetStmtAttr(hstmtDst, SQL_SOPT_SS_PARAM_FOCUS,
 (SQLPOINTER) 1, SQL_IS_INTEGER);
 // Col 1 - Bind start of PersonID array
 retcode = SQLBindParameter(hstmtDst, 1, SQL_PARAM_INPUT,
 SQL_C_LONG, SQL_INTEGER, 0, 0,
 &uiPersonID[1], 0, &lPersonID[1]);
 // Col 2 - Bind start of FirstName array
 retcode = SQLBindParameter(hstmtDst, 2, SQL_PARAM_INPUT,
 SQL_C_CHAR, SQL_CHAR,
 FIRSTNAME_LEN, 0, &strFirstName[1],
 FIRSTNAME_LEN, &lFirstName[1]);
 // Col 3 - Bind start of LastName array
 retcode = SQLBindParameter(hstmtDst, 3, SQL_PARAM_INPUT,
 SQL_C_CHAR, SQL_CHAR,
 LASTNAME_LEN, 0, &strLastName[1],
 LASTNAME_LEN, &lLastName[1]);
 // Col 4 - Bind start of Address array
 retcode = SQLBindParameter(hstmtDst, 4, SQL_PARAM_INPUT,
 SQL_C_CHAR, SQL_CHAR,
 ADDRESS_LEN, 0, &strAddress[1],
 ADDRESS_LEN, &lAddress[1]);
 // Col 5 - Bind start of City array
 retcode = SQLBindParameter(hstmtDst, 5, SQL_PARAM_INPUT,
 SQL_C_CHAR, SQL_CHAR,
 CITY_LEN, 0, &strCity[1],
 CITY_LEN, &lCity[1]);
 //
 // If there were further TVPs we would set the focus to the next one,
 // bind buffers to that, and so on on until all TVPs have bound buffers.
 // As we have no more, reset param focus to zero.
 //
 retcode = SQLSetStmtAttr(hstmtDst, SQL_SOPT_SS_PARAM_FOCUS,
 (SQLPOINTER) 0, SQL_IS_INTEGER);
 //
 // The data for each row of the TVP is represented by the nth element of
 // the arrays bound for columns FirstName, LastName, Address and City,
 // held in strFirtstName[n], strLastName[n], strAddress[n], and strCity[n]
 // Prompt for data, filling columns and rows until batchSize reached.
 // Data for the TVP starts in array posn 1 (0 is used for reading the
 // records from the source table).
 //
 retcode=SQL_SUCCESS;
 //
 // While exits via a break when no more rows to copy
 //
 while (1) {
 // Get next batch of records starting from last PersonID processed
 rowsFound=nextBatch (hdbc, lastPersonID, batchSize);
 //
 // Display the batch returned and at the same time set the length
 // variables that were bound in the SQLBindParameter() calls for the
 // TVP.
 //
 for (i=1;i<=rowsFound;i++) {
 // Person ID and length
 printf ("\n%i, ", uiPersonID[i]);
 lPersonID[i]=sizeof(uiPersonID[i]);
 // FirstName and length
 printf ("%.10s, ", strFirstName[i]);
 lFirstName[i]=strlen(strFirstName[i]);
 // LastName and length
 printf ("%.10s, ", strLastName[i]);
 lLastName[i]=strlen(strLastName[i]);
 // Address and length
 printf ("%.10s, ", strAddress[i]);
 lAddress[i]=strlen(strAddress[i]);
 // City and length
 printf ("%.10s", strCity[i]);
 lCity[i]=strlen(strCity[i]);
 // Remember current person ID. The PersonIDs are selected in
 // numeric ascending order (because of the ORDER BY in the select).
 // By keeping hold of the last one in this batch the next batch
 // starts at the next PersonID greater than this.
 lastPersonID=uiPersonID[i];
 }
 //
 // We now have arrays of PersonIDs, FirstNames, Lastnames, Addresses
 // and Cities with corresponding lengths for each.
 //
 // Set the StrLen_or_IndPtr value (lTVPRowsUsed) to the actual number
 // of rows to write (i.e. in the buffers) and execute the stored
 // procedure. Note this might be less than the batch size if this batch
 // is the last batch. Although just one parameter is passed, because
 // it is a TVP, several rows (like a table) can be passed as one
 // parameter (hence Table-Valued Parameter).
 //
 lTVPRowsUsed=rowsFound;
 retcode = SQLExecDirect(hstmtDst, "{CALL InsertFromTVP(?)}", SQL_NTS);
 CHECK_ERROR(retcode, "SQLExecDirect(InsertFromTVP)",
 hstmtDst, SQL_HANDLE_STMT);
 // Got last batch? If so break out of the loop, otherwise continue
 if (rowsFound<batchSize) break; else printf ("\n");
 }
exit:
 printf ("\nComplete.\n");
 // Free handles
 // Statement
 if (hstmtSrc != SQL_NULL_HSTMT)
 SQLFreeHandle(SQL_HANDLE_STMT, hstmtSrc);
 if (hstmtDst != SQL_NULL_HSTMT)
 SQLFreeHandle(SQL_HANDLE_STMT, hstmtDst);
 // Connection
 if (hdbc != SQL_NULL_HDBC) {
 SQLDisconnect(hdbc);
 SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
 }
 // Environment
 if (henv != SQL_NULL_HENV)
 SQLFreeHandle(SQL_HANDLE_ENV, henv);
 return 0;
}

Further information

Download ODBC Drivers for

Oracle, SQL Server, Salesforce, MongoDB, Access, Derby, InterBase, DB2, & more.

Learn More
Share:

AltStyle によって変換されたページ (->オリジナル) /