Beruflich Dokumente
Kultur Dokumente
Introduction
An Application Programming Interface (API) is a definition of the set of functions an application can use to access a system resource. ODBC is a database API based on the Call Level Interface (CLI) API definition published by the standards organizations X/Open and ISO/CAE. ODBC applications can access data in many of today's leading database management systems (DBMSs) by using ODBC drivers written to access those DBMSs. The application calls the ODBC API functions using a standard ODBC SQL syntax, then the ODBC driver makes any necessary translations to send the statement to the DBMS and presents the results back to the application. This paper describes how application programmers using the ODBC API can optimize access to Microsoft SQL Server when using the Microsoft SQL Server ODBC driver. The paper also discusses issues commonly raised by customers who have called Microsoft Support for help with the SQL Server ODBC driver. This paper is not a tutorial on ODBC programming in general, nor is it a comprehensive discussion about performance tuning on SQL Server. It assumes the reader is already familiar with ODBC programming and the use of SQL Server. For more information about ODBC, see the Microsoft ODBC 2.0 Programmer's Reference and SDK Guide available on MSDN and from Microsoft Press, and Inside ODBC by Kyle Geiger, also available from Microsoft Press. For more information about SQL Server, see the SQL Server documentation. Except where noted, users should assume that this paper is discussing the operation of Microsoft SQL Server version 6.5 and its associated version 2.65 ODBC driver. This paper uses the ODBC version 2.5 API functions because version 2.5 is the version used by most existing applications and is also the version of the ODBC SDK that ships with Microsoft SQL Server Workstation version 6.5. Programmers writing ODBC 3.0 applications should refer to the Microsoft ODBC 3.0 Software Development Kit and Programmer's Reference. Readers primarily interested in performance-related issues will find most of the useful information in the following sections of this paper: "General Good Practices" "Choosing a Cursor Option" "SQLExecDirect vs. SQLPrepare/SQLExecute" "Batching Procedure Calls" "Text and Image Data"
Architecture
The Microsoft SQL Server ODBC driver uses the standard SQL Server components for communicating from a client application to the database server. Rather than being implemented as a new layer over SQL Server's older native API, DB-Library, the ODBC driver writes directly to the same Network-Library (NetLibrary) layer used by DB-Library. The ODBC driver is implemented as a native API to SQL Server and is a
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
1/66
14/05/13
Library) layer used by DB-Library. The ODBC driver is implemented as a native API to SQL Server and is a functional replacement of the DB-Library DLL. The components involved in accessing a SQL Server from an ODBC application are described in the following sections.
Application
The application makes calls to the ODBC API using SQL statements written in either ODBC SQL syntax or SQL Server Transact-SQL syntax.
The network protocol stack transports the TDS packets between the client and the server. The protocol
2/66
14/05/13
The network protocol stack transports the TDS packets between the client and the server. The protocol stack has components on both the client and the server.
Server Net-Library
The server Net-Libraries work on the server, passing TDS packets back and forth between SQL Server and its clients. Each SQL Server can work simultaneously with any of the server Net-Libraries installed on the server.
SQL Server
SQL Server is the server engine that processes all queries from SQL Server clients.
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
3/66
14/05/13
native DBMS API, translating the ODBC statements coming from the application into the native DBMS API functions and SQL syntax. This translation effort adds extra processing compared with having the application call directly to the native API. This assumption is true for some ODBC drivers implemented over a native DBMS API, but the Microsoft SQL Server ODBC driver is not implemented this way. The Microsoft SQL Server ODBC driver is a functional replacement of DB-Library. The SQL Server ODBC driver works with the underlying Net-Libraries in exactly the same manner as the DB-Library DLL. The Microsoft SQL Server ODBC driver has no dependence on the DB-Library DLL, and the driver will function correctly if DB-Library is not even present on the client. Microsoft's testing has shown that the performance of ODBC-based and DB-Librarybased SQL Server applications is roughly equal. The following illustration compares the ODBC and DB-Library implementations.
Operating systems supported Windows Windows Windows 3.11 Windows NT3.5, 3.51, 4.0 95 for Workgroups 3.1
4/66
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
14/05/13
2.65.0240
12/30/96
6.5 SP2
Windows Windows Windows 3.11 Windows Windows Windows Windows 3.11 Windows Windows Windows Windows 3.11 Windows Windows Windows Windows 3.11 Windows Windows Windows Windows 3.11 Windows
NT3.5, 3.51, 4.0 95 for Workgroups 3.1 NT3.5, 3.51, 4.0 95 for Workgroups 3.1 NT3.5, 3.51, 4.0 95 for Workgroups 3.1 NT3.5, 3.51 95 for Workgroups 3.1 NT3.5, 3.51 95 for Workgroups 3.1
2.65.0213
07/30/96
6.5 SP1
2.65.0201
04/03/96
6.5
2.50.0126
08/17/95
6.0 4.21a
2.50.0121
06/07/95
6.0
6.0 4.21a
Note: None of the Microsoft SQL Server ODBC drivers listed is certified to work with Sybase SQL Servers. Applications needing to connect to Sybase SQL Servers must get an ODBC driver certified for use with Sybase from either Sybase or a third-party ODBC driver vendor. For more information about versions and Instcat.sql, see "Instcat.sql."
14/05/13
Windows NTuser-specific data sources and Windows 95 data sources On the Microsoft Windows NT operating system, user data sources are specific to the Windows NTaccount under which they were defined. User-specific data sources are not always visible to applications running as Windows NT services. Windows 95 data sources are stored in the following registry key: HKEY_CURRENT_USER\Software\ODBC\Odbc.ini. Windows NTsystem data sources On Windows NT, system data sources are visible to all Windows NTaccounts on the computer. System data sources are always visible to applications running as Windows NTservices. The ODBC driver manager that ships with Microsoft Office 97 also supports system data sources on Windows 95 clients. Windows NTsystem data sources are stored in the following registry key: HKEY_LOCAL_MACHINE\Software\ODBC\Odbc.ini. Information about the drivers installed on a client is stored in C:\Windows\System\Odbcinst.ini in Windows 3.x or Windows for Workgroups 3.x and in HKEY_LOCAL_MACHINE\Software\ODBC\Odbcinst.ini in Windows NTand Windows 95. Each driver needs to store driver-specific information in its data sources. When a user adds a data source using ODBC Administrator, the driver displays a dialog box, where the user specifies data source information. When a data source is defined with SQLConfigDataSource, the function accepts an attribute string parameter that can contain driver-specific keywords. All of the SQLConfigDataSource driver-specific keywords for the SQL Server ODBC driver have counterparts in the dialog box that displays when using ODBC Administrator. Here's an example SQLConfigDataSource call that sets up a SQL Server data source referencing a server using DHCP on TCP/IP:
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
6/66
14/05/13
example, Dbmssocn, not Dbmssocn.dll). The ADDRESS parameter is the network address of the Windows NTserver running SQL Server. If ADDRESS is present, it is always used as the network address for the connection. If ADDRESS is not present, then SERVER is used as the network address for the connection. Here's an example entry to make a named pipes connection to a server:
S E R V E R = x y z , N E T W O R K = d b n m p n t w , A D D R E S S = H R S e r v e r
S E R V E R = H R S e r v e r , N E T W O R K = d b n m p n t w
S E R V E R = t c p x y z , N E T W O R K = d b m s s o c n , A D D R E S S = 1 2 3 . 1 2 3 . 1 2 3 . 1 2 3 , 1 4 3 3
There are two special cases to consider: Connecting to a SQL Server running on the same computer as the client. The ODBC data source for this case is specified as:
S E R V E R = ( l o c a l ) , N E T W O R K = ( d e f a u l t ) , A D D R E S S = ( d e f a u l t )
When using this data source, the driver attempts to connect to a SQL Server on the same computer using Windows NTlocal-named pipes instead of a network implementation of named pipes. Setting up a data source that connects to a server using whatever Net-Library is currently set as the default on the client. An example of an entry for this case is:
S E R V E R = H R S e r v e r , N E T W O R K = ( d e f a u l t ) , A D D R E S S = ( d e f a u l t )
The default Net-Library is set using the SQL Server Client Configuration Utility. The SERVER, NETWORK, and ADDRESS parameters specified on SQL Server ODBC driver data sources operate the same way as the Server, DLL, and Connection String parameters specified for advanced entries
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
7/66
14/05/13
operate the same way as the Server, DLL, and Connection String parameters specified for advanced entries made with the SQL Server Client Configuration Utility. For more information about the advanced-entry parameters, see the Microsoft SQL Server Administrator's Companion. The same parameters can be specified in the data source creation dialog box displayed in ODBC Administrator. The relationship between the parameters is illustrated in the following table. SQLConfigDataSource SERVER NETWORK ADDRESS ODBC Administrator Server Network Library Network Address SQL Client Configuration Utility Server DLL Connection String
If a data source is defined with the SERVER, NETWORK, and ADDRESS parameters, a SQL Server advanced connection entry is made in the registry, and can be viewed using the SQL Client Configuration Utility.
DATABASE
This parameter specifies the default database for the ODBC data source.
LANGUAGE
This parameter specifies the default national language to use.
OEMTOANSI
This parameter specifies whether to convert extended characters to OEM values. SQL Server is usually run with one of three code pages: 437 code page. The default code page for U.S. MS-DOS computers. 850 code page. The code page typically used by UNIX systems. ISO 8859-1 (Lantin1 or ANSI) code page. The code page defined as a standard by the ANSI and ISO standards organizations. The default code page for U.S. Windows computers. Sometimes called the 1252 code page. The 437 and 850 code pages are sometimes collectively referred to as the OEM code pages. All three code pages define 256 different values to use in representing characters. The values from 0 to128 represent the same characters in all three code pages. The values from 129 to 255, which are known as the extended characters, represent different characters in all three code pages. Because ODBC applications are Windows applications, they generally use ANSI code page 1252. If they are communicating with a SQL Server also running ANSI code page 1252, there is no need for character-set conversion. If they connect to a server running a 437 or 850 code page however, the driver must be informed that it should convert extended characters from their 1252 values to 437 or 850 values before sending them to the server. In this case, the data source should have OEMTOANSI=YES. For a more indepth discussion of SQL Server code pages, see Microsoft Knowledge Base article Q153449.
TRANSLATIONDLL
This parameter specifies the name of the ODBC translation DLL to use with the data source.
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
TRANSLATIONNAME
8/66
14/05/13
TRANSLATIONNAME TRANSLATIONOPTION
This parameter specifies the name of the translator to use with the data source.
This parameter specifies whether translation should be done on the data going to SQL Server. YES specifies translation; NO specifies no translation. For more information about ODBC translation, see the ODBC 2.0 Programmer's Reference.
USEPROCFORPREPARE
This parameter specifies whether the driver generates stored procedures to support the ODBC SQLPrepare function. For more information, see "SQLExecDirect vs. SQLPrepare/SQLExecute." The following driver-specific SQLConfigDataSource keywords are new in SQL Server 6.5 SP2.
QuotedID
This parameter specifies whether the driver should issue a SET QUOTED IDENTIFIERS ON option when connecting to a SQL Server version 6.0 or later database. YES specifies QUOTED_IDENTIFIERS is ON; NO specifies the option is OFF. For more information, see "SET Options Used by the Driver."
AnsiNPW
This parameter specifies whether the driver should SET ON the ANSI_NULLS, ANSI_PADDING, and ANSI_WARNINGS options when connecting to a SQL Server version 6.5 or later database. YES specifies the options are ON; NO specifies they are OFF. For more information, see "SET Options Used by the Driver." The following driver-specific SQLConfigDataSource keywords are new in SQL Server 6.5.
QueryLogFile
This parameter specifies the file name the driver should use to log long-running queries. Include the full path name for the file. For more information, see "ODBC Driver Profiling Features."
QueryLog_ON
This parameter specifies whether the data source should do query profiling. 1 specifies profiling is done; omitting the parameter specifies no profiling. For more information, see "ODBC Driver Profiling Features."
QueryLogTime
This parameter specifies the interval for long-running queries. The interval is specified in milliseconds. If a query is outstanding for a period exceeding the QueryLogTime, it is written to the QueryLogFile. For more information, see "ODBC Driver Profiling Features."
StatsLogFile
This parameter specifies the file name the driver should use to log long performance statistics. Include the full path name for the file. For more information, see "ODBC Driver Profiling Features."
StatsLog_On
This parameter specifies whether the data source should log performance statistics. 1 specifies profiling is done; omitting the parameter specifies no profiling. For more information, see "ODBC Driver Profiling Features."
Trusted_Connection
This parameter specifies whether the data source should use trusted connections when connecting to SQL Server. 1 specifies trusted connections; omitting the parameter specifies no trusted connections. For more information, see "Integrated and Standard Security."
9/66
14/05/13
the same features that are controlled by the SQLConfigDataSource keywords earlier in this paper, although they have slightly different names. Many of the options are in the dialog box that displays when you click Options. To specify the query and performance profiling options, click Options, and then click Profiling.
R E T C O D E r e t c o d e ; U C H A R s z D S N [ M A X B U F L E N + 1 ]= " D R I V E R = { S Q LS e r v e r } ; S E R V E R = M y S e r v e r ; " " U I D = s a ; P W D = a s t r i n g ; A P P = G e n e r i c 3 2 ; D A T A B A S E = p u b s " ; U C H A R s z U I D [ M A X U I D + 1 ]=" s a " , s z A u t h S t r [ M A X A U T H S T R + 1 ]=" p a s s w o r d " , s z C o n n S t r O u t [ M A X B U F L E N + 1 ] ; S W O R D s w S t r L e n ; r e t c o d e=S Q L D r i v e r C o n n e c t ( h d b c 1 , N U L L , s z D S N , ( S W O R D ) s t r l e n ( s z D S N ) , s z C o n n S t r O u t , M A X B U F L E N , & s w S t r L e n , S Q L _ D R I V E R _ N O P R O M P T ) ;
The SQL Server ODBC driver supports three classes of keywords on SQLDriverConnect: The standard ODBC keywords The SQL Server ODBC driver supports the four standard ODBC SQLDriverConnect keywords: DSN, UID, PWD, and DRIVER. The driver-specific SQLConfigDataSource keywords On SQLDriverConnect the SQL Server ODBC driver supports all of the driver-specific keywords it supports for SQLConfigDataSource. See the list earlier in this paper for a description of these driver-specific keywords. The driver-specific keywords APP and WSID In addition to supporting the same driver-specific keywords as SQLConfigDataSource, SQLDriverConnect also supports the two driver-specific keywords APP and WSID.
APP
This keyword specifies the application name to be recorded in the program_name column in master.dbo.sysprocesses. APP is equivalent to a DB-Library application calling the DBSETLAPP function in C or the SQLSetLApp function in the Visual Basic programming system.
WSID
This keyword specifies the workstation name to be recorded in the hostname column in master.dbo.sysprocesses. WSID is equivalent to a DB-Library application calling the DBSETLHOST function in C or the SQLSetLHost function in Visual Basic.
Connection Messages
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 10/66
14/05/13
Connection Messages
The SQL Server ODBC driver returns SQL_SUCCESS_WITH_INFO on a successful SQLConnect, SQLDriverConnect, or SQLBrowseConnect. When an ODBC application calls SQLError after getting SQL_SUCCESS_WITH_INFO, it can receive the following messages: 5701indicates SQL Server initially putting the user's context into the default database defined at the server for the login ID used in the connection 5703indicates the language being used on the server If either the ODBC data source has a default database specified or the application specified the DATABASE keyword on SQLDriverConnect or SQLBrowseConnect, there will be a second 5701 message that indicates the user's context has been switched to the database requested. The following example shows these messages being returned on a successful connect by the System Administrator (SA) login. The SA login has its default database at the server defined as the master database, the server is running US English, and the connect used an ODBC data source that specified pubs as the default database.
F u l lC o n n e c t : s z S q l S t a t e=" 0 1 0 0 0 " ,* p f N a t i v e E r r o r=5 7 0 1 , s z E r r o r M s g = " [ M i c r o s o f t ] [ O D B CS Q LS e r v e rD r i v e r ] [ S Q LS e r v e r ] C h a n g e dd a t a b a s ec o n t e x tt o' m a s t e r ' . " s z S q l S t a t e=" 0 1 0 0 0 " ,* p f N a t i v e E r r o r=5 7 0 3 , s z E r r o r M s g = " [ M i c r o s o f t ] [ O D B CS Q LS e r v e rD r i v e r ] [ S Q LS e r v e r ] C h a n g e dl a n g u a g es e t t i n gt o' u s _ e n g l i s h ' . " s z S q l S t a t e=" 0 1 0 0 0 " ,* p f N a t i v e E r r o r=5 7 0 1 , s z E r r o r M s g = " [ M i c r o s o f t ] [ O D B CS Q LS e r v e rD r i v e r ] [ S Q LS e r v e r ] C h a n g e dd a t a b a s ec o n t e x tt o' p u b s ' . " S u c c e s s f u l l yc o n n e c t e dt oD S N' m y 6 0 s e r v e r ' .
Applications can ignore these 5701 and 5703 messages; they are informational only. Applications cannot, however, ignore a return of SQL_SUCCESS_WITH_INFO return code on the SQLConnect, SQLDriverConnect, or SQLBrowseConnect. This is because messages other than 5701 and 5703 that do require action may be returned. For example, if a driver connects to a SQL Server with outdated system stored procedures, one of the messages returned through SQLError is:
S q l S t a t e : 0 1 0 0 0 p f N a t i v e : 0 s z E r r o r M s g :" [ M i c r o s o f t ] [ O D B CS Q LS e r v e rD r i v e r ] T h eO D B C c a t a l o gs t o r e dp r o c e d u r e si n s t a l l e do ns e r v e r m y 4 2 1 s e r v e ra r ev e r s i o n0 2 . 0 0 . 4 1 2 7 ;v e r s i o n0 6 . 0 0 . 0 1 1 5 o rl a t e ri sr e q u i r e dt oe n s u r ep r o p e ro p e r a t i o n . P l e a s ec o n t a c ty o u rs y s t e ma d m i n i s t r a t o r . "
An application's error handling routines for SQL Server connections should call SQLError until it returns SQL_NO_DATA_FOUND and act on any messages other than the ones that return a pfNative code of 5701 or 5703.
SQL Server offers three security models for authenticating connection attempts:
11/66
14/05/13
SQL Server offers three security models for authenticating connection attempts: Standard security The SA defines SQL Server logins with passwords in SQL Server and then associates the logins with users in individual databases. With older versions of SQL Server, all connection attempts must specify a valid login and password. SQL Server version 6.0 or 6.5 also allows trusted connections to a server running standard security. SQL Server logins are separate from Windows NTuser IDs. Integrated security The SA defines logins for those Windows NTuser accounts that are allowed to connect to SQL Server. Users do not have to specify a separate login and password when they connect to SQL Server after logging on to the Windows NTnetwork. When they attempt to connect, the NetLibrary attempts a trusted connection to SQL Server. If the user's Windows NTaccount is one that the SA specified to SQL Server, the connection succeeds. Mixed security The SA defines both SQL Server logins and Windows NTaccounts as SQL Server logins. Users with validated Windows NTaccounts can connect using trusted connections; other users can connect using standard security with the SQL Server logins. The SQL Server ODBC driver always uses a trusted connection when connecting to a server running integrated security. The driver can also be instructed to open trusted connections when connecting to a server that is running with standard or mixed security. Only the named pipes or multiprotocol Net-Libraries support integrated security and trusted connections. There are two ways to tell the driver to use trusted connections: Driver-specific data source options When defining a data source using the ODBC Administrator, you can select Use Trusted Connection. When defining a data source using SQLConfigDataSource, an application can specify Trusted_Connection=1. Driver-specific connection options Before making a connect request, the application can set a driver-specific option:
S Q L S e t C o n n e c t O p t i o n ( h d b c ,S Q L _ I N T E G R A T E D _ S E C U R I T Y ,S Q L _ I S _ O N ) ;
Integrated security offers several benefits: Passwords do not need to be stored in the application. Passwords are never present in the SQL Server TDS packets. Integrated security is easy to administer because the SA can use the SQL Security Manager utility to create SQL Server logins from existing Windows NTaccounts.
Protocol Considerations
Integrated security is only available when using either the named pipes or multiprotocol Net-Libraries. When using the multiprotocol Net-Library, the SA can also configure the server to encrypt packets sent across the network, so that even users of network sniffers cannot see the data. The named pipes and multiprotocol Net-Libraries can also work with either a TCP/IP, SPX/IPX, or NetBEUI protocol stack. This means a client running only a TCP/IP protocol stack can use either the Windows sockets, named pipes, or
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 12/66
14/05/13
multiprotocol Net-Libraries. The Windows sockets (TCP/IP), SPX/IPX, Appletalk, DECNet, and Banyan Vines Net-Libraries only work with their single, associated, protocol stack. Due to their added functionality, such as the encryption feature, the multiprotocol Net-Libraries are somewhat slower than the others. Testing at Microsoft has found that the TCP/IP Net-Libraries are somewhat faster than the other Net-Libraries. Other considerations, however, such as database design, indexing, and the design of queries and applications, usually have a greater impact on performance than the choice of a Net-Library. Applications running against SQL Server 6.0 or 6.5 can sometimes improve their performance by resetting the TDS network packet size. The default packet size is set at the server, and is 4K. 4K generally gives the best performance. Applications can set the packet size themselves if testing shows that they perform better with a different packet size. ODBC applications can do this by calling SQLSetConnectionOption with the SQL_PACKET_SIZE option before connecting. Some applications may perform better with a larger packet size, but performance improvements are generally minimal for packet sizes larger than 8K.
C O N N E C T E DT OS Q LS E R V E R O D B CS Q LS e r v e rD r i v e rV e r s i o n :0 2 . 6 5 . 0 2 0 1 S Q LS e r v e rV e r s i o n :S Q LS e r v e rf o rW i n d o w sN T 6 . 5 0-6 . 5 0 . 2 0 1( I n t e lX 8 6 ) A p r31 9 9 60 2 : 5 5 : 5 3 C o p y r i g h t( c )1 9 8 8 1 9 9 7M i c r o s o f tC o r p o r a t i o n
If the connect attempt is not successful, odbcping displays the errors it receives. (The 6.0 version of odbcping does not display the Native Error code.) For example:
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
13/66
14/05/13
C O U L DN O TC O N N E C TT OS Q LS E R V E R S Q L S t a t e :0 1 0 0 0 N a t i v eE r r o r :2 E r r o rM e s s a g e :[ M i c r o s o f t ] [ O D B CS Q LS e r v e rD r i v e r ] [ d b n m p n t w ] C o n n e c t i o n O p e n( C r e a t e F i l e ( ) ) . S Q L S t a t e :0 8 0 0 1 N a t i v eE r r o r :6 E r r o rM e s s a g e :[ M i c r o s o f t ] [ O D B CS Q LS e r v e rD r i v e r ] [ d b n m p n t w ] S p e c i f i e dS Q LS e r v e rn o tf o u n d .
The pfNative (or Native Error) code is important in diagnosing connection problems. For more information, see "pfNative Error Codes."
Stored Procedures
Sites can realize performance gains by coding most of their SQL statements into stored procedures and having applications call the stored procedures rather than issuing the SQL statements themselves. This offers the following benefits: Higher performance The SQL statements are parsed and compiled only when the procedures are created, not when the procedures are executed by the applications. Reduced network overhead Having an application execute a procedure instead of sending sometimes complex queries across the network can reduce the traffic on the network. If an ODBC application uses the ODBC { CALL MyProcedure} syntax to execute a stored procedure, the ODBC driver makes additional optimizations that eliminate the need to convert parameter data (for more information, see "ODBC Call vs. Transact-SQL EXECUTE"). Better consistency The organization's business rules can be coded and debugged once in a stored procedure, and they will then be consistently applied by all of the applications. The site does not have to depend on all application programmers coding their SQL statements correctly in all the applications. Better accuracy Most sites will have their best SQL programmers developing stored procedures. This means that the SQL statements in procedures tend to be more efficient and have fewer errors than when the code is developed multiple times by programmers of varying skill levels. The Enterprise versions of the Microsoft Visual C++ development system and Microsoft Visual Basic programming system also offer a new SQL debugger tool. With SQL Debugger, programmers can use the standard debugger facilities of their programming environment, such as setting break points and watching
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 14/66
14/05/13
standard debugger facilities of their programming environment, such as setting break points and watching variables, to debug their SQL Server stored procedures.
Batches
An application that builds several SQL statements to execute realizes better performance if it batches the statements together and sends them to the server all at once. This will reduce the number of network roundtrips the application uses to perform the same work. For example:
The application uses SQLMoreResults to be positioned on the next result set when they are finished with the current result set.
Data Conversion
The ODBC functions dealing with moving data into or out of program variables, such as SQLBindCol, SQLBindParameter, and SQLGetData, allow implicit conversion of data types. For example, an application that displays a numeric column can ask the driver to convert the data from numeric to character:
15/66
14/05/13
Applications should minimize data conversions. Unless data conversion is a required part of the application, the application should bind columns to a program variable of the same data type as the column in the result set. If the application needs to have the data converted, it is more efficient for the application to ask the driver to do the data conversion than for the application to do it. The driver normally just transfers data directly from the network buffer to the application's variables. Requesting the driver to perform data translation forces the driver to buffer the data and use CPU cycles to perform the conversion.
Data Truncation
If an application attempts to retrieve data into a variable that is too small to hold it, the driver generates a warning. The driver must allocate memory for the warning messages and spend CPU resources on some error handling. This can all be avoided if the application allocates variables large enough to hold the data from the columns in the result set, or uses the SUBSTRING function in the select list to reduce the size of the columns in the result set.
Query Options
Timeout intervals can be adjusted to prevent problems. Also, having different settings for some ODBC statement and connection options among several open connection or statement handles can generate excess network traffic. Calling SQLSetConnectOption with fOption set to SQL_LOGIN_TIMEOUT controls the amount of time an application waits for a connection attempt to timeout while waiting to establish a connection (0 specifies an infinite wait). Sites with slow response times can set this value high to ensure connections have sufficient time to complete, but the interval should always be low enough to give the user a response in a reasonable amount of time if the driver cannot connect. Calling SQLSetStmtOption with fOption set to SQL_QUERY_TIMEOUT sets a query timeout interval to protect the server and the user from long running queries. Calling SQLSetStmtOption with fOption set to SQL_MAX_LENGTH limits the amount of text and image data that an individual statement can retrieve. Calling SQLSetStmtOption with fOption set to SQL_MAX_ROWS also limits a rowset to the first n rows if that is all the application needs. Note that setting SQL_MAX_ROWS causes the driver to issue a SET ROWCOUNT statement to the server, which will affect all SQL statements, including triggers and updates. Care should be used when setting these options, however. It is best if all statement handles on a connection handle have the same settings for SQL_MAX_LENGTH and SQL_MAX_ROWS. If the driver switches from a statement handle to another with different values for these options, the driver must generate the appropriate SET TEXTSIZE and SET ROWCOUNT statements to change the settings. The driver cannot put these statements in the same batch as the user SQL since the user SQL can contain a statement that must be the first statement in a batch, therefore the driver must send the SET TEXTSIZE and SET ROWCOUNT statements in a separate batch, which automatically generates an extra roundtrip to the server.
SET NOCOUNT
Applications can execute the Transact-SQL statement SET NOCOUNT ON. When this is set on, SQL Server does not return an indication of how many rows were affected by data-modification statements, or by any statements within procedures. When SET NOCOUNT is ON, the driver does not get the information it needs to return the number of rows affected should the application call SQLRowCount after a data-modification statement.
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 16/66
14/05/13
All statements executed in a stored procedure, including SELECT statements, generate an "x rows affected" message. Issuing a SET NOCOUNT ON at the start of a large stored procedure can significantly reduce the network traffic between the server and client and improve performance by eliminating these messages. These messages are typically not needed by the application when it is executing a stored procedure.
Cursors
Starting with SQL Server 6.0, the SQL Server ODBC driver supports the ODBC cursor options by using server cursors.
Cursor Types
The ODBC standard assumes that a cursor is automatically opened on each result set and, therefore, does not make a distinction between a result set and a cursor. SQL Server SELECT statements, however, always return a result set. A SQL Server cursor is a separate entity created when the application needs to perform cursor operations such as scrolling and positioned updates. In the ODBC model, all SQL statements return a result set within a cursor, and an application retrieves rows through the cursor using either SQLFetch or SQLExtendedFetch. Before executing an SQL statement, an ODBC application can call SQLSetStmtOption to set statement options that control the cursor's behavior. These are the default settings for the cursor options. Option SQL_CURSOR_TYPE SQL_CONCURRENCY SQL_ROWSET_SIZE Default SQL_CURSOR_FORWARD_ONLY SQL_CONCUR_READ_ONLY 1
When running with these default settings, the application can only use SQLFetch to fetch through the result set one row at a time from the start of the result set to the end. When running with these default settings, the SQL Server ODBC driver requests a default result set from the server. In a default result set, SQL Server sends the results back to the client in a very efficient, continuous stream. The calls to SQLFetch retrieve the rows out of the network buffers on the client. It is possible to execute a query with these default settings, and then change the SQL_ROWSET_SIZE after the SQLExecDirect or SQLExecute complete. In this case, SQL Server still uses a default result set to efficiently send the results to the client, but the application can also use SQLExtendedFetch to retrieve multiple rows at a time from the network buffers. An ODBC application can change the SQL_CURSOR_TYPE to request different cursor behaviors from the result set. The types of cursors that can be set are: Static cursors
S Q L _ C U R S O R _ T Y P E = S Q L _ C U R S O R _ S T A T I C
In a static cursor, the complete result set is built when the cursor is opened. The cursor does not reflect any changes made in the database that affect either the rows in the result set, or the values in the columns of those rows. In other words, static cursors always display the result set as it was when the cursor was opened. If new rows have been inserted that satisfy the conditions of the cursor's SELECT statement, they do not appear in the cursor. If rows in the result set have been updated, the
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 17/66
14/05/13
Using ODBC with Microsoft SQL Server SELECT statement, they do not appear in the cursor. If rows in the result set have been updated, the new data values do not appear in the cursor. Rows appear in the result set even if they have been deleted from the database. No UPDATE, INSERT, or DELETE operations are reflected in a static cursor (unless the cursor is closed and reopened), not even modifications made by the same user who opened the cursor. Static cursors are read-only.
Dynamic cursors
S Q L _ C U R S O R _ T Y P E = S Q L _ C U R S O R _ D Y N A M I C
Dynamic cursors are the opposite of static cursors; they reflect all changes made to the rows in their result set as the user scrolls around the cursor. In other words, the data values and membership of rows in the cursor can change dynamically on each FETCH. The cursor shows all DELETE, INSERT, and UPDATE statements either made by the user who opened the cursor or committed by other users. Dynamic cursors do not support FETCH ABSOLUTE because the size of the result set and the position of rows within the result set are not constant. The row that starts out as the tenth row in the result set may be the seventh row the next time a FETCH is performed. Forward-only cursors
S Q L _ C U R S O R _ T Y P E = S Q L _ C U R S O R _ F O R W A R D _ O N L Y
This cursor is similar to a dynamic cursor, but it only supports fetching the rows serially in sequence from the start to the end of the cursor. Keyset-driven cursor
S Q L _ C U R S O R _ T Y P E = S Q L _ C U R S O R _ K E Y S E T _ D R I V E N
With a keyset-driven cursor, the membership of rows in the result set and their order is fixed when the cursor is opened. Keyset-driven cursors are controlled through a set of unique identifiers (keys), known as the keyset. The keys are built from a set of columns that uniquely identify the rows. The keyset is the set of all the key values that made up the rows in the result set when the cursor was opened. Changes to data values in nonkeyset columns for the rows (made by the current user or committed by other users) are reflected in the rows as the user scrolls through the cursor. Inserts are not reflected unless the cursor is closed and reopened. Deletes generate an "invalid cursor position" error (SQLState S1109) if the application attempts to fetch the missing row. If an update is made to a key-column value, it operates like a delete of the old key value followed by an insert of the new key value, and the new key value is not visible to the cursor. Attempts to fetch the old key value generate the S1109 error, but the new key value is not visible to the cursor. Mixed cursors
S Q L _ C U R S O R _ T Y P E = S Q L _ C U R S O R _ K E Y S E T _ D R I V E N S Q L _ K E Y S E T _ S I Z E = n
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
18/66
14/05/13
SQL Server does not support mixed cursors. All ODBC cursors support the concept of a rowset, which is the number of rows returned on an individual SQLExtendedFetch. For example, if an application is presenting a 10-row grid to the user, the cursor can be defined with a rowset size of 10 to simplify mapping data into the grid.
S Q L _ C O N C U R R E N C Y=S Q L _ C O N C U R _ R E A D O N L Y
With this option set, the cursor does not support UPDATE, INSERT, or DELETE statements. Locks are not held on the underlying rows that make up the result set.
S Q L _ C O N C U R R E N C Y=S Q L _ C O N C U R _ V A L U E S
This option offers optimistic concurrency control. Optimistic concurrency control is a standard part of transaction control theory and is discussed in most papers and books on the subject. The application uses optimistic control when "optimistic" that there is a slight chance that anyone else may have updated the row in the interval between when the row is fetched and when the row is updated. When the cursor is opened in this mode, no locks are held on the underlying rows to maximize throughput. If the user attempts an UPDATE, the current values in the row are compared with the values retrieved when the row was fetched. If any of the values have changed, SQL Server returns an error. If the values are the same, the cursor engine performs the UPDATE. Selecting this option means the application must deal with an occasional error indicating that another user updated the row and changed the values. A typical action taken by an application that receives this error would be to refresh the cursor, to get the new values, and then let the user or application decide if the UPDATE should still be performed. Note that text and image columns are not used for concurrency comparisons.
S Q L _ C O N C U R R E N C Y=S Q L _ C O N C U R _ R O W V E R
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
This optimistic concurrency control option is based on row versioning. With row versioning, the underlying table must have a version identifier of some type that the cursor engine can use to determine whether the row has been changed since it was read into the cursor. In SQL Server, this is the facility offered by the timestamp data type. SQL Server timestamps are binary numbers that indicate the relative sequence of modifications in a database. Each database has a global current timestamp value, @@dbts, which is incremented with every change in the database. If a table has a timestamp column, then its timestamp column is updated with the current @@dbts value every time the row is updated. The cursor engine can then compare a row's current timestamp value with the timestamp value that was first retrieved into the cursor to determine whether the row has been updated. The engine does not have to compare the values in all columns, only the timestamp value. If an application
19/66
14/05/13
not have to compare the values in all columns, only the timestamp value. If an application requests SQL_CONCUR_ROWVER on a table that does not have a timestamp column, the cursor defaults to the values-based optimistic concurrency control, SQL_CONCUR_VALUES.
S Q L _ C O N C U R R E N C Y=S Q L _ C O N C U R _ L O C K
This option implements pessimistic concurrency control, in which the application attempts to lock the underlying database rows at the time they are read into the cursor result set. For cursors using server cursors, an update intent lock is placed on the page holding the row when it is read into the cursor. If the cursor is opened within a transaction, these intent-toupdate locks are held until the transaction is committed or rolled back. If the cursor has been opened outside a transaction, the lock is dropped when the next row is fetched. Thus, applications wanting full pessimistic concurrency control would typically open the cursor within a transaction. An update intent lock prevents any other task from acquiring an update intent or exclusive lock, which prevents any other task from updating the row. An update intent lock, however, will not block a shared lock, so it does not prevent other tasks from reading the row, unless the second task is also requesting a read with an update intent lock. In all of these concurrency options, when any row in the cursor is updated, SQL Server locks it with an exclusive lock. If the update has been done within a transaction, the exclusive lock is held until the transaction is terminated. If the update has been done outside of a transaction, the update is automatically committed when it is completed and the exclusive lock is freed. Because SQL Server must acquire an exclusive lock before it updates the row, positioned updates done through a cursor (just like standard updates) can be blocked by other connections holding a shared lock on the row.
Isolation Levels
The full locking behavior of cursors is based on an interaction between the concurrency options discussed above and the transaction isolation level set by the client. ODBC clients set the transaction isolation level by setting the connection option SQL_TXN_ISOLATION. Users should combine the locking behaviors of the concurrency and transaction isolation level options to determine the full locking behavior of a specific cursor environment. READ COMMITTED (The default for both SQL Server and ODBC)
S Q L _ T X N _ I S O L A T I O N=S Q L _ T X N _ R E A D _ C O M M I T E D
SQL Server acquires a shared lock while reading a row into a cursor but frees the lock immediately after reading the row. Because a shared lock request is blocked by an exclusive lock, a cursor is prevented from reading a row that another task has updated but not yet committed. READ UNCOMMITTED
S Q L _ T X N _ I S O L A T I O N=S Q L _ T X N _ R E A D _ U N C O M M I T E D
SQL Server requests no locks while reading a row into a cursor and honors no exclusive locks. This means that cursors can be populated with values that have already been updated but not yet committed. The user is bypassing all of SQL Server's locking transaction control mechanisms.
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 20/66
14/05/13
S Q L _ T X N _ I S O L A T I O N=S Q L _ T X N _ R E P E A T A B L E _ R E A D _ U N C O M M I T E D
Or
S Q L _ T X N _ I S O L A T I O N=S Q L _ T X N _ S E R I A L I Z A B L E
SQL Server still requests a shared lock on each row as it is read into the cursor as in READ COMMITTED, but if the cursor is opened within a transaction, the shared locks are held until the end of the transaction instead of being freed after the row is read. This is the same effect as specifying HOLDLOCK on a SELECT statement. Note that the ODBC API specifies additional transaction isolation levels, but these are not supported by SQL Server or the Microsoft SQL Server ODBC driver.
Server Cursors
Prior to version 6.0, SQL Server sent result sets back to clients using only one type of result set, the default result set. While the default result set is efficient at sending results back to clients, it only supports the characteristics of the default ODBC result set: forward-only, read-only, and a rowset size of one. Because of this, the Microsoft SQL Server ODBC drivers that shipped with SQL Server version 4.2x only supported the default ODBC settings. When using a default result set, there is only one roundtrip between the client and server; this occurs at the time the SQL statement is executed. After the statement is executed, the server sends the packets containing the results back to the client until all of the results have been sent back or the client has cancelled the results by calling SQLMoreResults. Calls to SQLFetch or SQLExtendedFetch do not generate roundtrips to the server, they just pull data from the client network buffers into the application. SQL Server 6.0 introduced cursors that are implemented on the server (server cursors). There are two types of server cursors: Transact-SQL cursors This type of cursor is based on the ANSI syntax for cursors and is meant to be used in Transact-SQL batches, primarily in triggers and stored procedures. Transact-SQL cursors are not intended to be used in client applications. API server cursors This type of cursor is created by either the DB-Library or ODBC APIs. The SQL Server ODBC driver that shipped with SQL Server 6.0 uses API server cursors to support the ODBC cursor options. Users access the functionality of API server cursors through either ODBC or DB-Library. If an ODBC application executes a statement with the default cursor settings, the SQL Server ODBC driver requests a default result set from SQL Server. If the application sets the ODBC cursor type options to anything other than the defaults, however, then the SQL Server ODBC driver instead requests the server to implement a server cursor with the same options requested by the application. Since the cursor is implemented on the server, the driver does not have to use memory on the client to build a client-based cursor. Server cursors
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 21/66
14/05/13
Using ODBC with Microsoft SQL Server server, the driver does not have to use memory on the client to build a client-based cursor. Server cursors can also reduce network traffic in cases where a user decides they do not need to retrieve an entire result set. For example, if a user opens a cursor with 1,000 rows but then finds what they were looking for in the first 100 rows they scroll through, the other 900 rows are never sent across the network.
When using server cursors, each call to SQLFetch, SQLExtendedFetch, or SQLSetPos causes a network roundtrip from the client to the server. All cursor statements must be transmitted to the server because the cursor is actually implemented on the server. One potential drawback of server cursors is that they currently do not support all SQL statements. Server cursors do not support any SQL statements that generate multiple result sets, therefore they cannot be used when the application executes either a stored procedure or a batch containing more than one select. If the application has set options that cause the driver to request an API server cursor, and then it executes a statement that server cursors do not support, the application gets an error:
Or
S Q L S t a t e :3 7 0 0 0 p f N a t i v e :1 6 9 3 8 s z E r r o r M s g :[ M i c r o s o f t ] [ O D B CS Q LS e r v e rD r i v e r ] [ S Q LS e r v e r ] s p _ c u r s o r o p e n . T h es t a t e m e n tp a r a m e t e rc a no n l y b eas i n g l es e l e c to ras i n g l es t o r e dp r o c e d u r e .
ODBC applications getting either of these errors when attempting to use server cursors with multiple statement batches or stored procedures should switch to using the ODBC default cursor options.
14/05/13
When the SQLExecDirect on hstmt1 is executed, the SQL Server ODBC driver issues a cursor open request. When SQL Server completes the cursor open, it considers the statement to be finished and allows the application to then issue a statement on another hstmt:
Once again, after the server has finished with the cursor open request issued by the client, it considers the statement to be completed. If at this point the ODBC application makes a fetch request as follows, the SQL Server ODBC driver sends SQL Server a cursor fetch for the first five rows of the result set:
S Q L E x t e n d e d F e t c h ( h s t m t 1 ,S Q L _ F E T C H _ N E X T ,1 ,. . . ) ;
After the server has transferred the five rows to the driver, it considers the fetch processing completed and accepts new requests. The application could then do a fetch on the cursor opened for the other statement handle:
S Q L E x t e n d e d F e t c h ( h s t m t 2 ,S Q L _ F E T C H _ N E X T ,1 ,. . . ) ;
SQL Server accepts this second statement on the connection handle because, as far as it is concerned, it has completed the last statement on the connection handle, which was the fetch of the first five rows of the rows for hstmt1.
14/05/13
would be preferred for a large result set where the user is likely to find their answer before retrieving many of the rows. Some simple rules to follow in choosing a cursor type are: Use default settings for singleton selects (returns one row), or other small result sets. It is more efficient to cache a small result set on the client and scroll through the cache. Use the default settings when fetching an entire result set to the client, such as when producing a report. After SQLExecute or SQLExecDirect, the application can increase the rowset size to retrieve multiple rows at a time using SQLExtendedFetch. The default settings cannot be used if the application is using positioned updates. The default settings cannot be used if the application is using multiple active statements. The default settings must be used for any SQL statement or batch of SQL statements that will generate multiple result sets. Dynamic cursors open faster than static or keyset-driven cursors. Internal temporary work tables must be built when static and keyset-driven cursors are opened but are not required for dynamic cursors. Use keyset-driven or static cursors if SQL_FETCH_ABSOLUTE is used. Static and keyset-driven cursors increase the usage of tempdb. Static server cursors build the entire cursor in tempdb; keyset-driven cursors build the keyset in tempdb. Each call to SQLFetch or SQLExtendedFetch causes a roundtrip to the server when using server cursors. Applications should minimize these roundtrips by using a reasonably large rowset size and by using SQLExtendedFetch instead of SQLFetch whenever possible.
The application can determine what type of cursor is now being used by calling SQLGetStmtOption with fOption set to SQL_CURSOR_TYPE. The cursor type conversion applies to only one statement. The next SQLExecDirect or SQLExecute will be done using the original statement cursor settings. Both SQL Server 6.0 and 6.5 have the following restrictions: If an SQL statement contains UNION, UNION ALL, GROUP BY, an outer join, or DISTINCT, all cursor types other than static are converted to static. If a keyset-driven cursor is requested and there is at least one table that does not have a unique index, the cursor is converted to a static cursor. SQL Server 6.0 has the following additional restrictions: If a dynamic cursor is requested and there is at least one table that does not have a unique index, the cursor is converted to a static cursor. If a dynamic cursor is requested and the SQL statement contains an ORDER BY that does not match a unique index or subquery, the cursor is converted to a static cursor.
14/05/13
S Q L P r e p a r e ( h s t m t ," s e l e c t* f r o ma u t h o r s " ,S Q L _ N T S ) ;
C R E A T EP R O C E D U R E# O D B C # n n n n n n n na s S E L E C T*F R O Ma u t h o r s
S Q L E x e c u t e ( h s t m t ) ;
The driver sends a remote stored procedure call to have the server run the #ODBC#nnnnnnnn procedure. Because a CREATE PROCEDURE statement essentially compiles an SQL statement into an execution plan, and an EXECUTE statement simply executes the precompiled plan, this meets the criteria for the SQLPrepare/SQLExecute mode. Excess or inappropriate use of SQLPrepare/SQLExecute degrades an application's performance. SQL Server applications should only use SQLPrepare/SQLExecute if they plan to execute a statement more than 3 to 5 times. If an application needs to execute a statement only once, using SQLPrepare/SQLExecute generates two roundtrips to the server: one to create the stored procedure and another to execute it. SQLExecDirect would only use one roundtrip and would also save the overhead of creating and storing a stored procedure. Excess use of SQLPrepare can also cause locking contention in the system tables in
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 25/66
14/05/13
tempdb as concurrent users continually try to create the stored procedures to support SQLPrepare. You may think that applications must use SQLPrepare/SQLExecute to use parameter markers, even if the application will only execute the statement once or twice. This is not true, applications can use parameter markers with SQLExecDirect by calling SQLBindParameter before SQLExecDirect. If an application will be run by many concurrent users and the users will all be using the same SQL statement, the best approach is to create the SQL statement as a permanent, parameterized, stored procedure and executed it with SQLExecDirect. Having many users concurrently issue SQLPrepare commands can create a concurrency problem on the system tables in tempdb. Even if each user is executing exactly the same statement, the SQL Server ODBC driver on each client is creating its own copy of a temporary stored procedure in tempdb. If the SQL statement is created as a parameterized stored procedure, however, the procedure is created only once. Each ODBC application does not have to create a new procedure for its exclusive use, it simply uses a copy of the permanent procedure's execution plan from the procedure cache. When used in the appropriate circumstances (to execute a single statement several times), SQLPrepare/SQLExecute can provide significant performance savings.
Impact on Tempdb
SQL Server 6.0 introduced temporary stored procedures, which are identified by having a number sign (#) as the first character in the name. These procedures operate like temporary tables and are automatically dropped by the server if the connection is broken. The SQL Server ODBC driver now creates the procedures that support SQLPrepare as temporary procedures. This makes it impossible for the ODBC-related stored procedures to build up as a result of broken network connections or client computer failures. However, the temporary stored procedures are always created in tempdb. This means that sites running SQL Server 6.0 or 6.5 with ODBC applications that use SQLPrepare must ensure that tempdb is large enough to hold the temporary procedures generated to support SQLPrepare. There is another factor to consider in relation to how many stored procedures exist in tempdb. ODBC applications call SQLSetConnectoption with fOption set to the driver-specific value SQL_USE_PROCEDURE_FOR_PREPARE and vParam set to either SQL_UP_OFF, SQL_UP_ON, or SQL_UP_ON_DROP to control the generation of temporary procedures. SQL_UP_OFF means that the driver does not generate stored procedures. SQL_UP_ON_DROP means that the driver generates stored procedures, and that they are dropped when the application does a SQLDisconnect, a SQLFreeStmt with fOption set to SQL_DROP, or the next time the application issues SQLPrepare on the same statement handle. SQL_UP_ON means that temporary procedures are created, but they are only dropped on a SQLDisconnect. SQL_UP_ON is the default setting. The driver can reuse procedures if an application re-prepares the same SQL statement, and most applications realize a performance boost because the driver is not having to continually drop stored procedures. This may result in a build up of #ODBC procedures in tempdb, however, from applications that never disconnect or applications that make heavy use of SQLPrepare. These applications should set SQL_UP_ON_DROP by calling SQLSetConnectOption. Starting with the driver that shipped in SQL Server 6.5 SP2, SQL_UP_ON_DROP is now an option that can be specified on data sources for the SQL Server ODBC driver.
14/05/13
The driver does not generate a stored procedure for a SQLPrepare that uses the ODBC CALL escape clause to call a stored procedure. On SQLExecute, the driver executes the called stored procedure (there is no need to create a temporary stored procedure). Calling either SQLDescribeCol or SQLDescribeParam before calling SQLExecute generates an extra roundtrip to the server. On SQLDescribeCol, the driver removes the WHERE clause from the query and sends it to the server with SET FMTONLY ON to get the description of the columns in the first result set returned by the query. On SQLDescribeParam, the driver calls the server to get a description of the columns in the tables referenced by the query. This method also has some restrictions, such as not being able to resolve parameters in subqueries.
Stored Procedures
This section discusses issues related to executing stored procedures using the SQL Server ODBC driver.
Using the ODBC syntax is recommended. The ODBC syntax, in addition to being more portable, offers improved features and performance over the EXECUTE statement. The SQL Server TDS protocol provides two methods of sending a procedure to the server: the procedure can be sent to the server as a regular SQL statement, or it can be sent as a TDS Remote Procedure Call (RPC). The TDS RPC syntax was originally defined for use by servers when one server is asked to execute a remote stored procedure on another server, but it can also be used by applications. Using the TDS RPC syntax means neither the driver nor the server need to perform any parameter conversions. This improves performance, especially for image parameters. The SQL Server ODBC driver uses the TDS RPC syntax if the application uses the ODBC CALL escape clause; it uses the regular SQL statement syntax if the application uses the Transact-SQL EXECUTE statement. Using the ODBC CALL escape clause also allows the application to retrieve output parameters and return codes from a stored procedure. Output parameter and return code processing is discussed below.
C R E A T EP R O C E D U R Eo d b c p r o c@ o p a r mi n tO U T P U TA S S E L E C Tn a m eF R O Ms y s u s e r sW H E R Eu i d<2 S E L E C T@ o p a r m=8 8 R E T U R N9 9 G O
The parameters and return codes can be bound to program variables in an ODBC application where the
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 27/66
14/05/13
ODBC with Microsoft SQL Server The parameters and return codes can be Using bound to program variables in an ODBC application where the application can reference them. For example, to execute the procedure above using the ODBC CALL escape clause and bind the return code and output parameters to program variables:
S Q L R E T U R N r c d ; D W O R D P r o c R e t=0 ,O P a r m=0 ; l o n g c b P r o c R e t=0 ,c b O P a r m=0 ; / /B i n dt h er e t u r nc o d e . r c d=S Q L B i n d P a r a m e t e r ( h s t m t ,1 ,S Q L _ P A R A M _ O U T P U T , S Q L _ C _ S L O N G ,S Q L _ I N T E G E R ,0 ,0 ,& P r o c R e t ,0 ,& c b P r o c R e t ) ; / /B i n dt h eo u t p u tp a r a m e t e r . r c d=S Q L B i n d P a r a m e t e r ( h s t m t ,2 ,S Q L _ P A R A M _ O U T P U T , S Q L _ C _ S L O N G ,S Q L _ I N T E G E R ,0 ,0 ,& O P a r m ,0 ,& c b O P a r m ; / /F i r s t?m a r k st h er e t u r nc o d e , / /s e c o n d?m a r k st h eo u t p u tp a r a m e t e r . r c d=( S Q L E x e c D i r e c t ( h s t m t ," { ?=c a l lo d b c p r o c ( ? ) } " ,S Q L _ N T S ;
SQL Server does not send back the values for the return code or output parameters until the end of all result sets for the procedure. The program variables ProcRet and OParm do not hold the output values of 99 and 88 until SQLMoreResults returns SQL_NO_DATA_FOUND.
S Q L S e t S t m t O p t i o n ( h s t m t , S Q L _ S O P T _ S S _ T E X T P T R _ L O G G I N G , S Q L _ T L _ O F F ) ;
This option should only be used for situations where the text or image data is not critical, and the data owners are willing to trade data recovery for higher performance.
14/05/13
image columns, the application cannot simply allocate a huge buffer, bind it to a parameter marker in an SQL statement, and then execute the statement. Whenever the size of the text or image data exceeds 400K (64K with SQL Server 4.21a), the application must use SQLGetData or SQLPutData with their Data-AtExecution options. Applications should always use Data-At-Execution if there is any possibility that the size of the data will exceed these limits. Data-At-Execution is described in the ODBC 2.0 Programmer's Reference; however, it remains one of the hardest parts of the ODBC API for an application programmer to learn. The Appendix of this paper contains the source code of two Win32 console applications, Getimage.c and Putimage.c, that illustrate using DataAt-Execution to read and write large amounts of image data. Text columns would use similar calls, the only difference would be binding between SQL_C_CHAR and SQL_LONGVARCHAR instead of SQL_C_BINARY and SQL_LONGVARBINARY. Programmers interested in working with text or image columns should look up the Data-At-Execution index entries of the ODBC 2.0 Programmer's Reference, then search for "text" and "image" in Microsoft SQL Server Programming ODBC for SQL Server.
Querying Metadata
This section discusses some common issues when getting metadata and catalog information from the driver.
Instcat.sql
Both the SQL Server system catalog stored procedures and the ODBC API catalog functions address the need of applications to retrieve catalog information from a database. Because there is a high correlation between the ODBC catalog functions and the SQL Server catalog stored procedures, the SQL Server ODBC driver implements many of the ODBC API catalog functions as calls to a corresponding SQL Server catalog procedure. The driver is therefore dependent on the catalog stored procedures in any SQL Server to which it connects. Each version of the SQL Server ODBC driver is developed in conjunction with a specific version of SQL Server. The proper operation of each driver version requires the versions of the catalog stored procedures associated with the specific version of SQL Server with which the driver was developed, or a later version of the procedures. For example, the 2.50.0121 driver was developed in conjunction with Microsoft SQL Server version 6.0, and requires either the versions of the system catalog stored procedures that were released with SQL Server 6.0, or with later versions, such as 6.5. The driver does not work properly with older versions of the catalog stored procedures, such as those in SQL Server version 4.21a. If a driver attempts to connect to a SQL Server running an older version of the catalog stored procedures than those required by the driver, the connection completes with SQL_SUCCESS_WITH_INFO and a call to SQLError returns the following message:
S q l S t a t e : 0 1 0 0 0 p f N a t i v e : 0 s z E r r o r M s g :" [ M i c r o s o f t ] [ O D B CS Q LS e r v e rD r i v e r ] T h eO D B C c a t a l o gs t o r e dp r o c e d u r e si n s t a l l e do ns e r v e r M y 4 2 1 S e r v e ra r ev e r s i o n0 2 . 0 0 . 4 1 2 7 ;v e r s i o n0 6 . 0 0 . 0 1 1 5 o rl a t e ri sr e q u i r e dt oe n s u r ep r o p e ro p e r a t i o n . P l e a s ec o n t a c ty o u rs y s t e ma d m i n i s t r a t o r . "
Although the connection is successful, the application may later encounter errors on calls to the ODBC API catalog functions. Sites running multiple versions of the driver against a server need to ensure that the server is running with at least the version of Instcat.sql associated with the newest ODBC driver that will connect to it. For example,
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 29/66
14/05/13
Using ODBC with Microsoft SQL Server at least the version of Instcat.sql associated with the newest ODBC driver that will connect to it. For example, a site running multiple version 6.0 servers could buy SQL Server version 6.5 and upgrade some clients to use the new 2.65.0201 driver that comes with version 6.5. The site would also need to run the 6.5 version of Instcat.sql against the 6.0 servers before the new driver can connect to them.
Installing a newer version of Instcat.sql into an older server does not break any existing applications connecting to that server, even ones still using the old drivers. It simply allows the applications using the new driver to operate correctly. Sites should run the Instcat.sql script at the server command prompt by using the isql utility.
C : \ > c d\ M s s q l \ I n s t a l l i s q l/ U s a/ P p a s s w o r d/ S s e r v e r n a m e/ i I n s t c a t . s q l/ o I n s t c a t . r p t
For more information about determining the version of Instcat.sql currently applied to a server, see Microsoft Knowledge Base article Q137636. For more information about the isql utility, see the Microsoft SQL Server Transact-SQL Reference.
Caching Metadata
If an application uses a particular set of metadata more than once, it will probably benefit by caching the information in private variables when it is first obtained. This eliminates the overhead of later calls to the ODBC catalog functions for the same information (which forces the driver to make roundtrips to the server).
SQLRowCount
If an ODBC application needs to know how many rows were affected by a data modification (UPDATE, INSERT, DELETE), it can call the SQLRowCount function after the modification completes. SQLRowCount is generally not filled after a SELECT statement, although it may be if the application is using server cursors. For more information, see Microsoft SQL Server Programming ODBC for Microsoft SQL Server.
When you issue SQLExecDirect, the driver builds a single batch calling the procedure five times, with a
30/66
14/05/13
When you issue SQLExecDirect, the driver builds a single batch calling the procedure five times, with a different element from the array associated with each procedure call. This is more efficient than sending five separate batches to the server. This process also works with procedures that take multiple parameters. Allocate an array for each parameter with the same number of elements in each array, then call SQLParamOptions specifying the number of elements.
S Q L _ A U T O C O M M I T _ O N S Q L S e t C o n n e c t O p t i o n ( h d b c , S Q L _ A U T O C O M M I T , S Q L _ A U T O C O M M I T _ O N ) ;
When autocommit is on, each statement is a separate transaction and is automatically committed when it completes successfully.
S Q L _ A U T O C O M M I T _ O F F S Q L S e t C o n n e c t O p t i o n ( h d b c , S Q L _ A U T O C O M M I T , S Q L _ A U T O C O M M I T _ O F F ) ;
When autocommit is turned off, the next statement sent to the database starts a transaction. The transaction remains in effect until the application calls SQLTransact with either the SQL_COMMIT or SQL_ROLLBACK options. The statement sent to the database after SQLTransact starts the next transaction. ODBC applications should not mix managing transactions through the ODBC autocommit options with calling the Transact-SQL transaction statements. If an application does this, it could generate undetermined results. The application should manage transactions in one of the following ways: Use SQLSetConnectOption to set the ODBC autocommit modes. Use Transact-SQL statements, such as BEGIN TRANSACTION. (The SQLSetConnectOption should be left at its default setting of autocommit on.) Applications should keep transactions as short as possible by not requiring user input while in a transaction. User input can take a long time, and all that time, the application is holding locks that may adversely impact other tasks needing the same data. An application should do all required queries and user interaction needed to determine the scope of the updates before starting the transaction. The application should then begin the transaction, do the updates, and immediately commit or rollback the transaction without user interaction.
14/05/13
within one transaction (autocommit off). When autocommit is on, each individual statement is committed by the server. Commits cause the server to flush out the modified log records. To improve performance, do all updates within one transaction and issue a single commit when all the changes have been made. Care must be taken to not include too many updates within one transaction, however. Performing many updates causes the transaction to be open longer and more pages to be locked with exclusive locks, which increases the probability that other users will be blocked by the transaction. Grouping modifications into a single transaction must be done in a way that balances multiuser concurrency with single-user performance. For applications that do not require a high degree of data accuracy, consider using the SQL_TXN_READ_UNCOMMITED transaction isolation level to minimize the locking overhead on the server.
These statements cause a subtotal calculation for the average price and sum of advances for each book type and then cause a final total sum of both the price and advance data. The following ODBCTest GetDataAll output shows how the ODBC driver presents these subtotals and totals back to the calling application as separate result sets intermixed with the primary result set:
" t i t l e " ," t y p e " ," p r i c e " ," a d v a n c e " " O n i o n s ,L e e k s ,a n dG " ," t r a d _ c o o k " F i f t yY e a r si nB u c k i " ," t r a d _ c o o k " S u s h i ,A n y o n e ? " ," t r a d _ c o o k 3r o w sf e t c h e df r o m4c o l u m n s . " A V G " ," S U M " 1 5 . 9 6 3 3 ,1 9 0 0 0 . 0 0 0 0 1r o wf e t c h e df r o m2c o l u m n s . " t i t l e " ," t y p e " ," p r i c e " ," a d v a n c e " " S i l i c o nV a l l e yG a s t r " ," m o d _ c o o k " T h eG o u r m e tM i c r o w a v " ," m o d _ c o o k 2r o w sf e t c h e df r o m4c o l u m n s . " A V G " ," S U M " 1 1 . 4 9 0 0 ,1 5 0 0 0 . 0 0 0 0 1r o wf e t c h e df r o m2c o l u m n s .
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
" ,1 9 . 9 9 0 0 ,. 0 0 0 0 " ,2 . 9 9 0 0 ,1 5 0 0 0 . 0 0 0 0
32/66
14/05/13
You can see from the output above that the driver presents the first result set for the rows from books having the first book type. It then produces a second result set with the two COMPUTE BY columns for the AVG(price) and SUM(advance) for this first set of books. Then it produces a third result set for the next group of books, and a fourth result set with the COMPUTE BY subtotals for that group. The driver keeps interleaving these result sets until the end, when it produces the final result set with the total for the COMPUTE SUM(price), SUM(advance) clause. Applications running SQL Server statements with COMPUTE BY and COMPUTE clauses must be coded to handle the multiple result sets returned by the driver. The Microsoft SQL Server ODBC driver only supports COMPUTE BY or COMPUTE with the default forward_only, read_only cursors with a rowset size of one. The driver implements all other cursor types (dynamic, static, or keyset-driven) using server cursors, which do not support COMPUTE BY or COMPUTE.
Distributed Transactions
The Microsoft Distributed Transaction Coordinator (MS DTC) allows applications to distribute transactions across two or more SQL Servers. It also allows applications to participate in transactions managed by transaction managers that comply with the X/Open DTP XA standard. (For more information, see What's New in SQL Server 6.5 and the Guide to Microsoft Distributed Transaction Coordinator in the SQL Server 6.5 manuals.) ODBC applications using the driver that ships with SQL Server 6.5 can participate in MS DTC transactions. Normally, all transaction management commands go through the ODBC driver to the server (see "Autocommit vs. ANSI Transaction Management"). The application starts a transaction by calling:
S Q L S e t C o n n e c t O p t i o n ( h d b c ,S Q L _ A U T O C O M M I T ,S Q L _ A U T O C O M M I T _ O F F ) ;
The application then performs the updates comprising the transaction and calls SQLTransact with either the SQL_COMMIT or SQL_ROLLBACK option. When using MS DTC, however, MS DTC is the transaction manager and the application no longer uses SQLTransact. The application: Connects to MS DTC using the MS DTC DtcSelectTransactionManager function. Calls SQLDriverConnect once for each connection. Calls the MS DTC ITransactionDispenser::BeginTransaction function to begin the MS DTC transaction and get a transaction object that represents the transaction. Enlists each ODBC connection in the MS DTC transaction by calling:
S Q L S e t C o n n e c t O p t i o n ( h d b c , S Q L _ C O P T _ S S _ E N L I S T _ I N _ D T C , ( U D W O R D ) p T r a n s a c t i o n ) ;
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
33/66
14/05/13
where pTransaction is a pointer to the transaction object. Performs all updates that make up the transaction. Calls the MS DTC function ITransaction::Commit or ITransaction::Rollback to commit or roll back the transaction. For information about MS DTC, see the Guide to Microsoft Distributed Transaction Coordinator, which includes a sample ODBC SQL Server MS DTC application.
Fallback Connections
SQL Server 6.5 introduced fallback support. In fallback support, one server is defined as a fallback server to another, primary, server. If the primary server fails for some reason, then applications can switch to the fallback server. This feature depends on special hardware and operating system support. For more information, see Microsoft SQL Server What's New in SQL Server 6.5. ODBC applications can take advantage of the SQL Server fallback feature by setting a driver-specific option before connecting:
S Q L S e t C o n n e c t O p t i o n ( h d b c , S Q L _ C O P T _ S S _ F A L L B A C K _ C O N N E C T , S Q L _ F B _ O N ) ;
Then, when the driver connects to the primary server, it retrieves from the primary server all the information it needs to connect to the fallback server and stores the information in the client's registry. If the application loses its connection to the primary server, it should clean up its current transaction and attempt to reconnect to the primary server. If the ODBC driver cannot reconnect to the primary server, it uses the registry information to attempt connecting to the fallback (secondary) server.
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
34/66
14/05/13
When SET STATISTICS TIME or SET SHOWPLAN are ON, SQLExecute and SQLExecDirect return SQL_SUCCESS_WITH_INFO, and, at that point, the application can retrieve the SHOWPLAN or STATISTICS TIME output by calling SQLError until it returns SQL_NO_DATA_FOUND. Each line of SHOWPLAN data comes back in the format:
The output of SET STATISTICS IO is not available until the end of a result set. To get STATISTICS IO output, the application calls SQLError at the time SQLFetch or SQLExtendedFetch returns SQL_NO_DATA_FOUND. The output of STATISTICS IO comes back in the format:
S Q L E x e c D i r e c t ( h s t m t ," d b c ct r a c e o n ( 3 6 0 4 ,4 0 3 2 ) " ,S Q L _ N T S ) ;
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
For example, after the above SQLExecDirect completes, the following subsequent call to SQLExecDirect returns SQL_SUCCESS_WITH_INFO:
35/66
14/05/13
DBCC execution output All other DBCC statements return data when they are executed. SQLExecDirect or SQLExecute returns SQL_SUCCESS_WITH_INFO, and the application retrieves the output by calling SQLError until it returns SQL_NO_DATA_FOUND. For example, the following statement returns SQL_SUCCESS_WITH_INFO:
S Q L E x e c D i r e c t ( h s t m t ," d b c cc h e c k t a b l e ( a u t h o r s ) " ,S Q L _ N T S ) ;
s z S q l S t a t e=" 0 1 0 0 0 " ,* p f N a t i v e E r r o r=2 5 3 6 , s z E r r o r M s g = " [ M i c r o s o f t ] [ O D B CS Q LS e r v e rD r i v e r ] [ S Q LS e r v e r ] C h e c k i n ga u t h o r s " s z S q l S t a t e=" 0 1 0 0 0 " ,* p f N a t i v e E r r o r=2 5 7 9 , s z E r r o r M s g = " [ M i c r o s o f t ] [ O D B CS Q LS e r v e rD r i v e r ] [ S Q LS e r v e r ] T h et o t a ln u m b e ro fd a t ap a g e si nt h i st a b l ei s1 . " s z S q l S t a t e=" 0 1 0 0 0 " ,* p f N a t i v e E r r o r=7 9 2 9 , s z E r r o r M s g = " [ M i c r o s o f t ] [ O D B CS Q LS e r v e rD r i v e r ] [ S Q LS e r v e r ] T a b l eh a s2 3d a t ar o w s . " s z S q l S t a t e=" 0 1 0 0 0 " ,* p f N a t i v e E r r o r=2 5 2 8 s z E r r o r M s g = " [ M i c r o s o f t ] [ O D B CS Q LS e r v e rD r i v e r ] [ S Q LS e r v e r ] D B C Ce x e c u t i o nc o m p l e t e d .I fD B C Cp r i n t e de r r o rm e s s a g e s , s e ey o u rS y s t e mA d m i n i s t r a t o r . "
14/05/13
SQL_SUCCESS_WITH_INFO:
The timing of calling SQLError is critical when output from PRINT or RAISERROR statements are included in a result set. The call to SQLError to retrieve the PRINT or RAISERROR output must be made immediately after the statement that receives SQL_ERROR or SQL_SUCCESS_WITH_INFO. This is straightforward when only a single SQL statement is executed, as in the examples above. In these cases, the call to SQLExecDirect or SQLExecute returns SQL_ERROR or SQL_SUCCESS_WITH_INFO and SQLError can then be called. It is less msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 37/66
14/05/13
or SQLExecute returns SQL_ERROR or SQL_SUCCESS_WITH_INFO and SQLError can then be called. It is less straightforward when coding loops to handle the output of a batch of SQL statements or when executing SQL Server stored procedures. In this case, SQL Server returns a result set for every SELECT statement executed in a batch or stored procedure. If the batch or procedure contains PRINT or RAISERROR statements, the output for these is interleaved with the SELECT statement result sets. If the first statement in the batch or procedure is a PRINT or RAISERROR, the SQLExecute or SQLExecDirect returns SQL_SUCCESS_WITH_INFO or SQL_ERROR and the application needs to call SQLError until it returns SQL_NO_DATA_FOUND to retrieve the PRINT or RAISERROR information. If the PRINT or RAISERROR statement comes after other SQL statements (such as a SELECT), then the PRINT or RAISERROR information is returned when SQLFetch or SQLExtendedFetch is called for the result set, before the PRINT or RAISERROR returns SQL_NO_DATA_FOUND or SQL_ERROR. For example, in the following procedure, the SQLExecute or SQLExecDirect call returns SQL_SUCCESS_WITH_INFO and a call to SQLError at that point returns the first print message. If the ODBC application then processes through the result set using SQLFetch, the application can get the second print statement by calling SQLError when SQLFetch returns SQL_NO_DATA_FOUND:
S Q L S e t C o n n e c t O p t i o n ( h d b c ,S Q L _ A S Y N C _ E N A B L E ,S Q L _ A S Y N C _ E N A B L E _ O N ) ; S Q L S e t C o n n e c t O p t i o n ( h d b c ,S Q L _ A S Y N C _ E N A B L E ,S Q L _ A S Y N C _ E N A B L E _ O F F ) ; S Q L S e t S t m t O p t i o n ( h d b c ,S Q L _ A S Y N C _ E N A B L E ,S Q L _ A S Y N C _ E N A B L E _ O N ) ; S Q L S e t S t m t O p t i o n ( h d b c ,S Q L _ A S Y N C _ E N A B L E ,S Q L _ A S Y N C _ E N A B L E _ O F F ) ;
When an application calls an ODBC function in synchronous mode, the driver does not return control to the application until it is notified that the server has completed the command. When operating asynchronously, the driver immediately returns control to the application, even before sending the command to the server. The driver sets the return code to SQL_STILL_EXECUTING. The
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 38/66
14/05/13
Using ODBC with Microsoft SQL Server sending the command to the server. The driver sets the return code to SQL_STILL_EXECUTING. The application is then free to perform other work.
To test for completion of the command, make the same function call with the same parameters to the driver. If the driver is still waiting for an answer from the server, it will again return SQL_STILL_EXECUTING. The application must keep testing the command periodically until it returns something other than SQL_STILL_EXECUTING. When the application gets some other return code, even SQL_ERROR, the command has completed. Sometimes a command is outstanding for a long time. If the application needs to cancel the command without waiting for a reply, it can do so by calling SQLCancel with the same statement handle as the outstanding command. This is the only time SQLCancel should be used. Some programmers use SQLCancel when the application has processed part way through a result set and they want to cancel the rest of the result set. SQLMoreResults or SQLFreeStmt with fOption set to SQL_CLOSE should be used to cancel the remainder of an outstanding result set, not SQLCancel.
Multithread Applications
The SQL Server ODBC driver is a fully multithreaded driver. Writing a multithread application is an alternative to using asynchronous calls to have multiple ODBC calls outstanding. A thread can make a synchronous ODBC call, and other threads can process while the first thread is blocked waiting for the response to its call. This model is more efficient than making asynchronous calls because it eliminates the overhead of the repeated ODBC function calls testing for SQL_STILL_EXECUTING to see if the function has completed. Asynchronous mode is still an effective method of processing. The performance improvements of a multithread model are not enough to justify rewriting asynchronous applications. If users are converting DBLibrary applications that use the DB-Library asynchronous model, it is easier to convert them to the ODBC asynchronous model. Multithread applications need to have coordinated calls to SQLError. After a message has been read from SQLError, it is no longer available to the next caller of SQLError. If a connection or statement handle is being shared between threads, one thread may read a message needed by the other thread.
S E TQ U O T E D _ I D E N T I F I E RO N S E TT E X T S I Z E2 1 4 7 4 8 3 6 4 7 S E TA N S I _ D E F A U L T SO N S E TC U R S O R _ C L O S E _ O N _ C O M M I TO F F S E TI M P L I C I T _ T R A N S A C T I O N SO F F
14/05/13
S E TA N S I _ N U L L _ D F L T _ O NO N S E TT E X T S I Z E2 1 4 7 4 8 3 6 4 7 S E TQ U O T E D _ I D E N T I F I E RO N S E TA R I T H A B O R TO N
S E TT E X T S I Z E2 1 4 7 4 8 3 6 4 7 S E TA R I T H A B O R TO N
The driver issues these statements itself; the ODBC application does nothing to request them. Setting these options allows ODBC applications using the driver to be more portable because the driver's behavior then matches the ANSI standard. DB-Library based applications, including the SQL Server utilities, generally do not turn these options on. Sites observing different behavior between ODBC or DB-Library clients when running the same SQL statement should not assume this points to a problem with the ODBC driver. They should first rerun the statement in the DB-Library environment (such as ISQL/w) with the same SET options as would be used by the SQL Server ODBC driver. Since the SET options can be turned on and off at any time by users and applications, developers of stored procedures and triggers should also take care to test their procedures and triggers with the SET options listed above turned both on and off. This ensures that the procedures and triggers work correctly regardless of what options a particular connection may have SET on when they invoke the procedure or trigger. The SET options used by the version 2.65 driver when connected to SQL Server 6.5 has the net effect of setting on three more ANSI options than in the 6.0 environment: ANSI_NULLS, ANSI_PADDING, and ANSI_WARNINGS. These options can cause problems in existing stored procedures and triggers. The version 2.65.0240 driver that shipped with SQL Server 6.5 SP2 allows data sources and connection statements to turn these options off. For more information, see Microsoft Knowledge Base article Q149921. The version 2.50 driver that shipped with SQL Server 6.0 also sets on the QUOTED_IDENTIFIER option. With this option set on, SQL statements should comply with the ANSI rule that character data strings be enclosed in single quotes and that only identifiers, such as table or column names, be enclosed in double quotes:
For more information about working with QUOTED_IDENTIFIER, see Microsoft Knowledge Base article Q156501. Like the ANSI options noted above, the version 2.65.0240 driver that shipped with SQL Server 6.5 SP2 allows SQLDriverConnect, SQLBrowseConnect, and data sources to specify that QUOTED_IDENTIFIERS not be turned on.
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 40/66
14/05/13
ODBC applications should not use the Transact-SQL SET statement to turn these options on or off. They should only set these options in either the data source or the connection options. The logic in the driver depends on it correctly knowing the current state of the SET options. If the application issues the SET statements itself, the driver may generate incorrect SQL statements due to not knowing that the option has been changed.
Diagnostic Messages
This section discusses how to interpret the error messages that are returned by the SQL Server ODBC driver. All ODBC functions have return codes. The ODBC header files have #define statements that equate the return codes to literals, such as SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, and SQL_ERROR. If a function returns SQL_SUCCESS_WITH_INFO, it means the function was successful but there is information available. If a function returns SQL_ERROR, it means the function failed and there is information available indicating the nature of the problem. To get these messages, the application can call SQLError. SQLError returns three parameters that have important information: SQLStatea 5-byte character string with an ODBC error code. pfNativea signed doubleword holding whatever error code is returned by the native database. szErrorMsga character string holding a header identifying the source of the error and the text of the error message.
[ M i c r o s o f t ] [ O D B CD r i v e rM a n a g e r ]
[ M i c r o s o f t ] [ O D B CC u r s o rL i b r a r y ]
[ M i c r o s o f t ] [ O D B CS Q LS e r v e rD r i v e r ]
If there are no other nodes identifying other components, these are errors encountered by the driver.
[ M i c r o s o f t ] [ O D B CS Q LS e r v e rD r i v e r ] [ N e t L i b r a r y n a m e ]
These are errors encountered by the Net-Library, where Net-Libraryname is the name of a SQL Server Net-Library (see "Setup and Connecting" for a list of the names). This also includes
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 41/66
14/05/13
SQL Server Net-Library (see "Setup and Connecting" for a list of the names). This also includes errors raised from the underlying network protocol because these errors are reported to the driver from the Net-Library. In these errors, the pfNative code contains the actual error returned by the network. (For more information about pfNative codes, see "pfNative Error Codes," later in this paper.) The remainder of the message contains two parts: the Net-Library function called, and (within parenthesis afterward) the underlying network API function called.
[ M i c r o s o f t ] [ O D B CS Q LS e r v e rD r i v e r ] [ S Q LS e r v e r ]
These are errors encountered by SQL Server. In this case, the pfNative parameter is the SQL Server error code. For example, when an application attempts to open a named-pipe connection to a server that is currently shut down, the error string returned is:
[ M i c r o s o f t ] [ O D B CS Q LS e r v e rD r i v e r ] [ d b n m p n t w ] C o n n e c t i o n O p e n( C r e a t e F i l e ( ) )
This indicates that the driver called the dbnmpntw ConnectionOpen function and that dbnmpntw in turn called the named-pipe API function CreateFile.
dbmsvinn, dbmsvin3
42/66
14/05/13
dbmsvinn, dbmsvin3 These codes, returned from Banyan Vines, are listed in the Vines Client Developer's Guide. dbmsrpcn, dbmsrpc3 These codes, returned by the RPC API, are listed in the RPC section of Winerror.h.
14/05/13
The ODBC trace facility traces all calls made to any ODBC data source on the client. It records ODBC calls immediately after they come into the Driver Manager from the application. This is helpful in debugging problems that the Driver Manager may have when connecting to a driver. This is a fairly minimal trace, however, and is used only when the second tool, ODBCSpy, is not available. Here's an example of an ODBC Driver Manager trace output:
A lot of information is missing from this output. There is no indication of the return code for each function call. There is no way to tell if the SQLDriverConnect call was successful; however, the fact that the next call was to SQLError could indicate some problem. Since the trace does not show what szErrorMsg string or SQLState value was returned by SQLError, there is no way to tell what the problem might have been. The fact that the application went on to allocate a statement handle and execute a statement seems to show that no major problem was encountered. When Driver Manager tracing is on, all calls to ODBC drivers on that client are traced. There is no way to trace only a specific data source.
ODBCSpy Trace
The ODBCSpy utility ships with the ODBC SDK and can be used to get an informative trace of all the ODBC calls made to a specific ODBC data source. ODBCSpy traces calls as they are passed from the Driver Manager to the ODBC driver. It shows all of the parameters passed for each call to the driver and the information returned from the driver. If an error is encountered, ODBCSpy calls SQLError for all error messages returned and logs all information about the errors in the trace. Here's an ODBCSpy trace of the same SQLError call traced in the example above:
S Q L E r r o r N U L L 0 x 0 1 0 1 0 0 0 0 N U L L [ 5 ] 0 1 0 0 0 5 7 0 1 [ 8 5 ] [ M i c r o s o f t ] [ O D B CS Q LS e r v e rD r i v e r ] [ S Q LS e r v e r ]C h a n g e dd a t a b a s ec o n t e x tt o' m a s t e r ' 5 1 2 8 5 S Q L _ S U C C E S S
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
This trace output includes more useful information. It shows that the SQLError function itself returned SQL_SUCCESS. (The entry for SQLDriverConnect would have shown that it returned SQL_SUCCESS_WITH_INFO, not SQL_ERROR.) The trace also shows that SQLError returned a SQLState of
44/66
14/05/13
SQL_SUCCESS_WITH_INFO, not SQL_ERROR.) The trace also shows that SQLError returned a SQLState of 01000, a pfNative of 5701, and a szErrorMsg string that indicates SQL Server has changed the connection context to the master database. There are also third-party ODBC tracing tools available.
SQL Trace
SQL Trace, a trace utility introduced in SQL Server 6.5, uses Open Data Services to intercept and trace all SQL statements coming in to SQL Server. SQL Trace is extremely valuable for determining if a problem is due to the Transact-SQL statements the driver generates to support the ODBC commands coming from the application. A programmer can use ODBCSpy to see exactly what comes from the application to the SQL Server ODBC driver, and then use SQL Trace to see what the driver actually sends to the server. If an application does:
SQL Trace can be used to dynamically trace statements coming in from different clients to a server. Sites that have servers earlier than SQL Server 6.5 can use an older, unsupported version of the utility called SQLEye. SQLEye is available on the Microsoft TechNet compact disc.
S Q L E x e c D i r e c t ( h s t m t ," d b c ct r a c e o n ( 3 6 0 5 ,4 0 3 2 ,1 ) " ,S Q L _ N T S ) ;
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
45/66
14/05/13
For more information about trace flags, see the SQL Server documentation.
S Q L S e t C o n n e c t O p t i o n ( h d b c , S Q L _ C O P T _ S S _ P E R F _ Q U E R Y _ L O G , ( U L O N G ) " c : \ \ o d b c q r y . l o g " ) ;
S Q L S e t C o n n e c t O p t i o n ( h d b c , S Q L _ C O P T _ S S _ P E R F _ Q U E R Y _ I N T E R V A L , 1 ) ;
The number specified is in seconds, so the call shown above causes all queries that do not return within one second to be logged. Note: The query profiling interval in a data source is specified in units of milliseconds. After these options are enabled, the application can turn logging on and off dynamically by executing:
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 46/66
14/05/13
S Q L S e t C o n n e c t O p t i o n ( h d b c , S Q L _ C O P T _ S S _ P E R F _ Q U E R Y , S Q L _ P E R F _ S T A R T ) ; S Q L S e t C o n n e c t O p t i o n ( h d b c , S Q L _ C O P T _ S S _ P E R F _ Q U E R Y , S Q L _ P E R F _ S T O P ) ;
Note that this option is global to the application; therefore, after the option has been started for any of the SQL Server ODBC connections, long-running queries from all SQL Server ODBC connections open from the application are logged.
S Q L S e t C o n n e c t O p t i o n ( h d b c , S Q L _ C O P T _ S S _ P E R F _ D A T A , S Q L _ P E R F _ S T A R T ) ; S Q L S e t C o n n e c t O p t i o n ( h d b c , S Q L _ C O P T _ S S _ P E R F _ D A T A , S Q L _ P E R F _ S T O P ) ;
Performance statistics are recorded in a data structure named sqlperf (for an explanation of the sqlperf variables, see the appendix). The statistics are global for all connections made through the driver by the application. For example, if the application starts the performance statistics and opens three connections, the statistics are global for all three connections. If an application wants to log performance data to a file, the following command creates the log file:
S Q L S e t C o n n e c t O p t i o n ( h d b c , S Q L _ C O P T _ S S _ P E R F _ D A T A _ L O G , ( U L O N G ) " c : \ \ o d b c p e r f . l o g " ) ;
The log file is a tab-delimited text file that can be opened in Microsoft Excel (specify tab delimited in the wizard that appears). Most other spreadsheet products also support opening a tab-delimited text file. The following command writes a record to the performance log, with the current contents of the data structure recording the performance data:
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 47/66
14/05/13
S Q L S e t C o n n e c t O p t i o n ( h d b c , S Q L _ C O P T _ S S _ P E R F _ D A T A _ L O G _ N O W , ( U L O N G ) N U L L ) ;
The application does not need to set up a performance log; it could instead pull the performance data by using SQLGetConnectOption to get a pointer to the sqlperf structure. This structure is declared in a typedef in the Odbcss.h header file. The following statements are an example of pulling the statistics into the application:
If the application uses a data source that has the performance-statistics profiling option activated, the driver writes the statistics header information to the log file and starts accumulating the statistics in its internal data structure when the application makes its first connection using the driver. When the last connection to the SQL Server ODBC driver from the application is closed, the driver writes out the global, accumulated, performance statistics.
Profiling Considerations
The fact that profiling is global to the driver governs the behavior of the log files. When an application connects to a data source that specifies profiling, the driver starts a log file and begins logging information from all connections active from the application to the SQL Server ODBC driver from that point forward. Even connections to SQL Server data sources that do not specify profiling are recorded because the profiling is done globally for the driver. If the application does a SQLFreeEnv, the ODBC Driver Manager unloads the driver. At this point, both the long-running query log and the performance statistics logs hold the information from the old connections. If the application then makes another connection to the data source that specifies profiling, the driver is reloaded, and it overwrites the old copy of the log file. If an application connects to a data source that specifies profiling, and then a second application connects to the same data source, the second application does not get control of the log file and therefore is not able to log any performance statistics or long-running queries. If the second application makes the connection after the first application disconnects, the driver overwrites the first application's log file with the one for the second application. Note that if an application connects to a data source that has either the long-running query or performance statistics enabled, the driver returns SQL_ERROR if the application calls SQLSetConnectOption to enable logging. A call to SQLError then returns the following message:
S Q L S t a t e :0 1 0 0 0 ,p f N a t i v e=0
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 48/66
14/05/13
Conclusion
This paper explains the interaction of Microsoft SQL Server and its associated ODBC driver. This knowledge helps you produce efficient ODBC applications that optimize the interaction between the driver and the server. It helps you avoid code that results in poor performance and describes how to take advantage of the unique features of Microsoft SQL Server and the ODBC driver. This paper also helps programmers and administrators to diagnose problems encountered by ODBC applications running on Microsoft SQL Server.
References
This section lists additional sources of information on Microsoft SQL Server and ODBC.
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
49/66
14/05/13
ISBN 0-07-911880-1
Database Developer's Guide With Visual C++ Roger Jennings and Peter Hipson Sam's Publishing ISBN 0-672-30613-1 Teach Yourself ODBC Programming in 21 Days Bill Whiting, Bryan Morgan, and Jeff Perkings Sam's Publishing ISBN 0-672-30609-3
Appendix
The appendix first defines all of the driver-specific options defined in Odbcss.h and then has two sample applications that illustrate processing text and image data.
Odbcss.h
Odbcss.h is a header file containing the definitions used for all of the driver-specific options in the SQL Server ODBC driver. Odbcss.h is distributed with SQL Server Workstation and with SQL Server 6.5 SP2. The version distributed with SP2 has a few extra connection options related to controlling the ANSI options used by the driver. The list below relates to the 6.5 SP2 version of Odbcss.h.
SQLSetConnectOption
The following options can be set on using SQLSetConnectOption. The bulleted literals are specified as the fOption parameter; the literals grouped under each bulleted fOption are specified as vParam.
S Q L _ C O P T _ S S _ R E M O T E _ P W D
(replaces SQL_REMOTE_PWD)
S Q L _ C O P T _ S S _ U S E _ P R O C _ F O R _ P R E P
(replaces SQL_USE_PROCEDURE_FOR_PREPARE) Controls generation of stored procedures on SQLPrepare. vParam value SQL_UP_OFF SQL_UP_ON SQL_UP_ON_DROP Description Do not generate procedures on SQLPrepare. Generate procedures for SQLPrepare; do not drop until SQLDisconnect. Generate procedures for SQLPrepare; drop on SQLFreeStmt (SQL_DROP), SQLDisconnect, or next SQLPrepare.
SQL_UP_DEFAULT = SQL_UP_ON
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 50/66
14/05/13
SQL_UP_ON
S Q L _ C O P T _ S S _ I N T E G R A T E D _ S E C U R I T Y
(replaces SQL_INTEGRATED_SECURITY) Controls use of integrated security when connecting to SQL Server. vParam value SQL_IS_OFF SQL_IS_ON SQL_IS_DEFAULT = SQL_IS_OFF Description Integrated security isn't used. Integrated security is used.
S Q L _ C O P T _ S S _ P R E S E R V E _ C U R S O R S
(replaces SQL_PRESERVE_CURSORS) Controls whether cursors are dropped at the end of a transaction. vParam value SQL_PC_OFF SQL_PC_ON SQL_PC_DEFAULT= SQL_PC_OFF Description Cursors are closed on SQLTransact. Cursors remain open on SQLTransact.
S Q L _ C O P T _ S S _ U S E R _ D A T A
S Q L _ C O P T _ S S _ A N S I _ O E M
51/66
14/05/13
SQL_AO_DEFAULT = SQL_AO_OFF, unless DSN OEM/ANSI checkbox is selected. vParam value SQL_AO_OFF ANSI/OEM SQL_AO_ON ANSI/OEM Description Translation is not performed. Translation is performed.
S Q L _ C O P T _ S S _ E N L I S T _ I N _ D T C
Controls enlistment in a distributed transaction managed by the Microsoft Distributed Transaction Coordinator. vParam value SQL_DTC_DONE Description Delimits end of distributed transaction.
S Q L _ C O P T _ S S _ E N L I S T _ I N _ X A
Enlists in a distributed transaction managed by a transaction manager that complies with the X/Open XA standard. The vParam value is a pointer to a variable defined using the structure: typedef struct SQLXaTranTAG { void FAR *transManager; void FAR *xaTransID; ULONGxaTransIDLength; DWORD dwErrorInfo; } SQLXaTran;
S Q L _ C O P T _ S S _ C O N N E C T I O N _ D E A D
Used to see if connection is still active. vParam value SQL_CD_FALSE SQL_CD_TRUE Description Connection is open/available. Connection is closed/dead.
S Q L _ C O P T _ S S _ F A L L B A C K _ C O N N E C T
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 52/66
14/05/13
Controls the use of SQL Server Fallback Connections. vParam value SQL_FB_OFF SQL_FB_ON SQL_FB_DEFAULT = SQL_FB_OFF Description Fallback connections are disabled. Fallback connections are enabled.
S Q L _ C O P T _ S S _ P E R F _ D A T A
Controls the logging of driver performance data. vParam value SQL_PERF_START SQL_PERF_STOP Description Starts the driver sampling performance data. Stops the counters from sampling performance data.
S Q L _ C O P T _ S S _ P E R F _ D A T A _ L O G
Specifies the file in which to log performance data. The vParam value is a pointer to a null-terminated string that contains the file name.
S Q L _ C O P T _ S S _ P E R F _ Q U E R Y _ I N T E R V A L
Specifies the interval for the trigger point to log a long-running query. The vParam value is an integer specifying the interval in seconds.
S Q L _ C O P T _ S S _ P E R F _ Q U E R Y _ L O G
Specifies the file in which to log long running queries. The vParam value is a pointer to a null-terminated string that contains the file name.
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
53/66
14/05/13
S Q L _ C O P T _ S S _ P E R F _ Q U E R Y
Controls the logging of long running queries. vParam value SQL_PERF_START SQL_PERF_STOP Description Starts the driver logging long-running queries. Stops the counters from logging long-running queries.
S Q L _ C O P T _ S S _ P E R F _ D A T A _ L O G _ N O W
Instructs the driver to write a performance statistics record to the log. The vParam value is NULL.
S Q L _ C O P T _ S S _ Q U O T E D _ I D E N T
Controls setting of QUOTED_IDENTIFIER (can only be set before connecting). vParam value SQL_QI_OFF SQL_QI_ON SQL_QI_DEFAULT = SQL_QI_ON Description Quoted identifiers are not supported. Quoted identifiers are supported.
S Q L _ C O P T _ S S _ A N S I _ N P W
Controls setting of ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS (can only be set before connecting). vParam value SQL_AD_OFF SQL_AD_ON SQL_AD_DEFAULT = SQL_AD_ON Description The ANSI options are not set on. The ANSI options are set on.
SQLSetStmtOption msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
54/66
14/05/13
SQLSetStmtOption
The following options can be set on using SQLSetStmtOption. The bulleted literals are specified as the fOption parameter, the literals grouped under each bulleted fOption are specified as vParam.
S Q L _ S O P T _ S S _ T E X T P T R _ L O G G I N G
(replaces SQL_TEXTPTR_LOGGING) Controls logging of text/image operations. vParam value SQL_TL_OFF SQL_TL_ON SQL_TL_DEFAULT = SQL_TL_ON Description No logging on text-pointer operations. Logging occurs on text-pointer operations.
S Q L _ S O P T _ S S _ H I D D E N _ C O L U M N S
Expose FOR BROWSE hidden columns. vParam value SQL_HC_OFF SQL_HC_ON Description BROWSE columns are hidden. BROWSE columns are exposed.
S Q L _ S O P T _ S S _ N O B R O W S E T A B L E
SET NOBROWSETABLE option. vParam value SQL_NB_OFF SQL_NB_ON Description NO_BROWSETABLE is off. NO_BROWSETABLE is on.
SQLGetStmtOption
If SQLGetStmtOption is called with fOption = SQL_SOPT_SS_CURRENT_COMMAND, the driver returns an integer to pvParam indicating which command in the batch is the one whose results are being processed. The first command in the batch is 1.
SQLColAttributes
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 55/66
14/05/13
The following options can be set on using SQLColAttributes. The bulleted literals are specified as the fDescType parameter; the literals grouped under each bulleted fDescType are the values returned as pfDesc.
S Q L _ C A _ S S _ C O L U M N _ S S T Y P E
The base data type of a SQL Server result column. pfDesc values: SQLBINARY, SQLBIT, SQLCHAR, SQLDATETIME, SQLDATETIM4, SQLDATETIMN, SQLDECIMAL, SQLDECIMALN, SQLFLT4, SQLFLT8, SQLFLTN, SQLIMAGE, SQLINT1, SQLINT2, SQLINT4, SQLINTN, SQLMONEY, SQLMONEY4, SQLMONEYN, SQLNUMERIC, SQLNUMERICN SQLTEXT, SQLVARBINARY, SQLVARCHAR
S Q L _ C A _ S S _ C O L U M N _ U T Y P E
The user-defined data type of a SQL Server result column. pfDesc values: SQLudtBINARY, SQLudtBIT, SQLudtCHAR, SQLudtDATETIME, SQLudtDATETIM4, SQLudtDATETIMN, SQLudtDECML, SQLudtDECMLN, SQLudtFLTN, SQLudtFLT4, SQLudtFLT8, SQLudtIMAGE, SQLudtINT1, SQLudtINT2, SQLudtINT4, SQLudtINTN, SQLudtMONEY, SQLudtMONEY4, SQLudtMONEYN, SQLudtNUM, SQLudtNUMN SQLudtSYSNAME, SQLudtTEXT, SQLudtTIMESTAMP, SQLudtVARBINARY, SQLudtVARCHAR,
S Q L _ C A _ S S _ N U M _ O R D E R S
S Q L _ C A _ S S _ C O L U M N _ O R D E R
The SELECT list column ID of a column that appears in the SQL statement ORDER BY clause. pfDesc returns the column ID.
S Q L _ C A _ S S _ C O L U M N _ V A R Y L E N
pfDesc is TRUE if the column's data can vary in length, otherwise FALSE.
S Q L _ C A _ S S _ N U M _ C O M P U T E S
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
56/66
14/05/13
pfDesc returns the number of compute clauses in the current result set.
S Q L _ C A _ S S _ C O M P U T E _ I D
S Q L _ C A _ S S _ C O M P U T E _ B Y L I S T
Returns a bylist: an array of the column IDs of the columns participating in a Transact-SQL COMPUTE BY clause. pfDesc returns a pointer to the bylist for a compute row.
S Q L _ C A _ S S _ C O L U M N _ I D
pfDesc returns the SELECT list column ID to which a COMPUTE BY aggregate refers.
S Q L _ C A _ S S _ C O L U M N _ O P
Identifies the aggregate operation the COMPUTE BY applied to a column. pfDesc returns: SQLAOPANY SQLAOPAVG (AVG()) SQLAOPCNT (COUNT()) SQLAOPMIN (MIN()) SQLAOPMAX (MAX()) SQLAOPNOOP SQLAOPSUM (SUM())
S Q L _ C A _ S S _ C O L U M N _ S I Z E
S Q L _ C A _ S S _ C O L U M N _ H I D D E N
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 57/66
14/05/13
The column is hidden. Applies only if the SELECT was FOR BROWSE and the driver-specific statement option SQL_SOPT_SS_HIDDEN_COLUMNS is set to SQL_HC_ON.
S Q L _ C A _ S S _ C O L U M N _ K E Y
S Q L _ C A _ S S _ B A S E _ C O L U M N _ N A M E
SQLGetInfo
If SQLGetInfo is called with fInfoType set to SQL_INFO_SS_NETLIB_NAME, rgbInfoValue returns the name of the Net-Library used to connect to SQL Server.
SQLPerf Structure
The meaning of the variables defined in the sqlperf structure are given in this section. These descriptions also apply to the statistics recorded in the performance log file. For a description of how to gather these statistics, see "Diagnosing and Profiling Applications."
Description
The minimum resolution of the server's clock time in milliseconds. This is usually reported as 0 (zero). The only time this statistic should be considered is if the number reported is large. If the minimum resolution of the server clock is larger than the likely interval for some of the timer-based statistics, those statistics may be inflated. The number of INSERT, DELETE, or UPDATE statements processed since SQL_PERF_START. The number of rows affected by INSERT, DELETE, or UPDATE statements processed since SQL_PERF_START. The number of SELECT statements processed since SQL_PERF_START. The number of rows selected since SQL_PERF_START. The number of user transactions since SQL_PERF_START. For example, suppose an application had run the following statements: SQLSetConnectOption(hdbc, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_ON);
58/66
SQLidu
SQLiduRows
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
14/05/13
ODBC with Microsoft SQL Server SQL_AUTOCOMMIT, Using SQL_AUTOCOMMIT_ON); SQLTransact(henv, hbdc,SQL_COMMIT); SQLTransact(henv, hdbc, SQL_ROLLBACK); This constitutes two user transactions. Even though the second transaction was rolled back, it still counted as a transaction. Also, when an ODBC application is running with SQL_AUTOCOMMIT_ON, each individual command is considered a transaction.
The number of SQLPrepare functions executed since SQL_PERF_START. The number of SQLExecDirect functions executed since SQL_PERF_START. The number of SQLExecute functions executed since SQL_PERF_START. The number of times the driver has opened a server cursor since SQL_PERF_START. The number of rows in the result sets opened by cursors since SQL_PERF_START. Description The number of rows actually retrieved through the driver from cursors since SQL_PERF_START. Here is the equation used to figure the percentage of cursor used: PercentCursorUsed = CursorUsed/CursorSize For example, if an application causes the driver to open a server cursor to do "select count(*) from authors," 23 rows are in the result set for the select. If the application then only fetches three of these rows, CursorUsed/CursorSize is 3/23, so PercentCursorUsed is 13.043478. Here is the equation used to figure the average fetch time: AvgFetchTime = SQLFetchTime/SQLFetchCount Here is the equation used to figure average cursor size: AvgCursorSize = CursorSize/CursorOpens Here is the equation used to figure average number of cursors used: AvgCursorUsed = CursorUsed/CursorOpens The cumulative amount of time it took fetches against server cursors to complete. The number of fetches done against server cursors since SQL_PERF_START. The number of statement handles currently open on all connections open in the driver. The maximum number of concurrently opened statement handles since SQL_PERF_START. The number of statement handles that have been opened since SQL_PERF_START.
PercentCursorUsed
AvgFetchTime
AvgCursorSize
AvgCursorUsed
MaxOpenStmt
SumOpenStmt
Connection Statistics
These variables profile the connections to SQL Server opened by the application.
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 59/66
14/05/13
Description The current number of active connection handles the application has open to the server. The maximum number of concurrent connection handles opened since SQL_PERF_START. The sum of the number of connection handles that have been opened since SQL_PERF_START. The sum of the amount of time for which all of the connections have been opened since SQL_PERF_START. For example, if an application opened 10 connections and maintained each connection for 5 seconds, then SumConnectionTime would be 50 seconds. Here is the equation used to figure average time connections are open: AvgTimeOpened = SumConnectionsOpened/ SumConnectionTime
MaxConnectionsOpened
SumConnectionsOpened
SumConnectionTime
AvgTimeOpened
Network Statistics
The network packet statistics reported by the driver relate to the TDS packets (for more information about TDS, see "Architecture"). The size of a TDS packet is either the server's default setting specified in sp_configure 'network packet size' or what the ODBC client might request through:
S Q L S e t C o n n e c t O p t i o n ( h d b c ,S Q L _ P A C K E T _ S I Z E ,N N N N )
These packets may be larger than the size of the network packets actually sent by the underlying protocol stack (such as TCP/IP or SPX/IPX). The SQL Server Net-Library DLLs and the underlying protocol stack are the components that map the TDS packets onto the network packets, but this is hidden from both the SQL Server ODBC driver and the DB-Library DLL. Network statistics ServerRndTrips BuffersSent
Description The number of times the driver sent commands to the server and got a reply back. The number of TDS packets sent to SQL Server by the driver since SQL_PERF_START. Large commands may take multiple buffers, so if a large command is sent to the server that filled six packets, ServerRndTrips would be incremented by one, BuffersSent by six. The number of TDS packets received by the driver from SQL Server since the application started using the driver. The number of bytes of data sent to SQL Server in TDS packets since the application started using the driver. The number of bytes of data in TDS packets received by the driver from SQL Server since the application started using the driver.
60/66
BuffersRec
BytesSent
BytesRec
Time Statistics
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
14/05/13
Time Statistics
These are the time statistics. Time statistics MsExecutionTime Description
The cumulative amount of time the driver spent doing its processing since SQL_PERF_START, including the time it spent waiting for replies from the server. The cumulative amount of time the driver spent waiting for replies from the server.
MsNetworkServerTime
C R E A T ET A B L Ee m p 2( n a m eC H A R ( 3 0 ) ,a g eF L O A T , b i r t h d a yD A T E T I M E ,B i g B i nI M A G E )
If Putimage is compiled and run first, then Getimage can be used to read the data. To confirm that all 300,000 bytes of image data has been entered by Putimage, run the following from ISQL/w:
S E L E C Tn a m e ,a g e ,b i r t h d a y ,B i n L e n=d a t a l e n g t h ( B i g B i n ) F R O Me m p 2
Note: Some of the error checking has been removed for clarity. Also, both programs use the same function, ProcessLogMessages, whose source code has been deleted from Getimage.c to save space.
Putimage.c
/ /S a m p l ea p p l i c a t i o nt ow r i t eS Q L _ L O N G V A R B I N A R Yd a t au s i n gS Q L P u t D a t a . / /A s s u m e sD S Nh a st a b l e : / / C R E A T ET A B L EE M P 2( N A M EC H A R ( 3 0 ) ,A G EF L O A T , / / B i g B i nI M A G E ) # i n c l u d e< s t d i o . h > # i n c l u d e< s t r i n g . h > # i n c l u d e< w i n d o w s . h > # i n c l u d e< s q l . h > # i n c l u d e< s q l e x t . h > # d e f i n eM A X D S N 2 5 # d e f i n eM A X U I D 2 5 # d e f i n eM A X A U T H S T R 2 5 # d e f i n eM A X B U F L E N 2 5 5 # d e f i n eS I Z E O F T E X T 3 0 0 0 0 0 H E N V h e n v=S Q L _ N U L L _ H E N V ; H D B C h d b c 1=S Q L _ N U L L _ H D B C ; H S T M T h s t m t 1=S Q L _ N U L L _ H S T M T ; c h a r l o g s t r i n g [ M A X B U F L E N ]=" " ; v o i d P r o c e s s L o g M e s s a g e s ( H E N Vp l m _ h e n v ,H D B Cp l m _ h d b c ,
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 61/66
14/05/13
v o i d
P r o c e s s L o g M e s s a g e s ( H E N Vp l m _ h e n v ,H D B Cp l m _ h d b c , H S T M Tp l m _ h s t m t ,c h a r* l o g s t r i n g ) ; i n tm a i n ( ) { R E T C O D Er e t c o d e ; U C H A R s z D S N [ M A X D S N + 1 ]=" a b 6 5 d e f " , s z U I D [ M A X U I D + 1 ]=" s a " , s z A u t h S t r [ M A X A U T H S T R + 1 ]=" p a s s w o r d " ; / /S Q L B i n d P a r a m e t e rv a r i a b l e s . S D W O R D c b T e x t S i z e ,l b y t e s ; / / S Q L P a r a m D a t av a r i a b l e . P T R p P a r m I D ; / / S Q L P u t D a t av a r i a b l e s . U C H A R D a t a [ ]= " a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z " " a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z " " a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z " " a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z " " a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z " " a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z " " a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z " " a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z " " a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z " " a b c d e f g h i j k l m n o p q r s t u v w x y z " ; S D W O R D c b B a t c h=( S D W O R D ) s i z e o f ( D a t a ) 1 ; / /A l l o c a t et h eO D B Ce n v i r o n m e n ta n ds a v eh a n d l e . r e t c o d e=S Q L A l l o c E n v( & h e n v ) ; / /A l l o c a t eO D B Cc o n n e c t i o na n dc o n n e c t . r e t c o d e=S Q L A l l o c C o n n e c t ( h e n v ,& h d b c 1 ) ; r e t c o d e=S Q L C o n n e c t ( h d b c 1 ,s z D S N ,( S W O R D ) s t r l e n ( s z D S N ) , s z U I D ,( S W O R D ) s t r l e n ( s z U I D ) , s z A u t h S t r , ( S W O R D ) s t r l e n ( s z A u t h S t r ) ) ; / /P r i n ti n f om e s s a g e sr e t u r n e d . i f(( r e t c o d e! =S Q L _ S U C C E S S )& & ( r e t c o d e! =S Q L _ S U C C E S S _ W I T H _ I N F O )){ P r o c e s s L o g M e s s a g e s ( h e n v , h d b c 1 , h s t m t 1 , " S Q L C o n n e c t ( )F a i l e d \ n \ n " ) ; r e t u r n ( 9 ) ; } e l s e{ P r o c e s s L o g M e s s a g e s ( h e n v , h d b c 1 , h s t m t 1 , " \ n C o n n e c tS u c c e s s f u l \ n \ n " ) ; } / /A l l o c a t eas t a t e m e n th a n d l e . r e t c o d e=S Q L A l l o c S t m t ( h d b c 1 , & h s t m t 1 ) ; / /L e tO D B Ck n o wt o t a ll e n g t ho fd a t at os e n d . l b y t e s=( S D W O R D ) S I Z E O F T E X T ; c b T e x t S i z e=S Q L _ L E N _ D A T A _ A T _ E X E C ( l b y t e s ) ;
/ /B i n dt h ep a r a m e t e r . r e t c o d e=S Q L B i n d P a r a m e t e r ( h s t m t 1 , / /h s t m t 1 , / /i p a r S Q L _ P A R A M _ I N P U T , / /f P a r a m T y p e S Q L _ C _ B I N A R Y , / /f C T y p e S Q L _ L O N G V A R B I N A R Y , / /F S q l T y p e msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
62/66
14/05/13
S Q L _ L O N G V A R B I N A R Y , / /F S q l T y p e l b y t e s , / /c b C o l D e f 0 , / /i b S c a l e ( V O I D* ) 1 , / /r g b V a l u e 0 , / /c b V a l u e M a x & c b T e x t S i z e ) ; / /p c b V a l u e i f(( r e t c o d e! =S Q L _ S U C C E S S )& & ( r e t c o d e! =S Q L _ S U C C E S S _ W I T H _ I N F O )){ P r o c e s s L o g M e s s a g e s ( h e n v ,h d b c 1 ,h s t m t 1 , " S Q L B i n d P a r a mh s t m t 1F a i l e d \ n \ n " ) ; r e t u r n ( 9 ) ; } r e t c o d e=S Q L E x e c D i r e c t ( h s t m t 1 , " I N S E R TI N T OE M P 2V A L U E S ( ' J O H NS M I T H ' ,2 7 . 3 ,? ) " , S Q L _ N T S ) ; i f(( r e t c o d e! =S Q L _ S U C C E S S )& & ( r e t c o d e! =S Q L _ S U C C E S S _ W I T H _ I N F O )& & ( r e t c o d e! =S Q L _ N E E D _ D A T A )){ P r o c e s s L o g M e s s a g e s ( h e n v ,h d b c 1 ,h s t m t 1 , " S Q L E x e c u t eF a i l e d \ n \ n " ) ; r e t u r n ( 9 ) ; } / /G e tI Do fp a r a m e t e rt h a tn e e d sd a t a . r e t c o d e=S Q L P a r a m D a t a ( h s t m t 1 ,& p P a r m I D ) ; / /I fd a t ai sn e e d e df o rt h eD a t a A t E x e c u t i o np a r a m e t e r : i f( r e t c o d e= =S Q L _ N E E D _ D A T A ){ / /S e n da l lb u tt h ef i n a lb a t c h . w h i l e( l b y t e s>c b B a t c h ){ S Q L P u t D a t a ( h s t m t 1 ,D a t a ,c b B a t c h ) ; l b y t e s=c b B a t c h ; } / /E n dw h i l e . / /P u tf i n a lb a t c h . S Q L P u t D a t a ( h s t m t 1 ,D a t a ,l b y t e s ) ; } e l s e{/ /I fn o tS Q L _ N E E D _ D A T A ,i ss o m ee r r o r . P r o c e s s L o g M e s s a g e s ( h e n v ,h d b c 1 ,h s t m t 1 , " S Q L P u t D a t aF a i l e d \ n \ n " ) ; r e t u r n ( 9 ) ; } / /e n di f / /M a k ef i n a lS Q L P a r a m D a t ac a l lt os i g n a le n do fd a t a . r e t c o d e=S Q L P a r a m D a t a ( h s t m t 1 ,& p P a r m I D ) ; i f(( r e t c o d e! =S Q L _ S U C C E S S )& & ( r e t c o d e! =S Q L _ S U C C E S S _ W I T H _ I N F O )& & ( r e t c o d e! =S Q L _ N E E D _ D A T A )){ P r o c e s s L o g M e s s a g e s ( h e n v ,h d b c 1 ,h s t m t 1 , " S Q L P a r a m D a t aF a i l e d \ n \ n " ) ; r e t u r n ( 9 ) ; } / *C l e a nu p .* / S Q L F r e e S t m t ( h s t m t 1 ,S Q L _ D R O P ) ; S Q L D i s c o n n e c t ( h d b c 1 ) ; S Q L F r e e C o n n e c t ( h d b c 1 ) ; S Q L F r e e E n v ( h e n v ) ; r e t u r n ( 0 ) ; } v o i dP r o c e s s L o g M e s s a g e s ( H E N Vp l m _ h e n v ,H D B Cp l m _ h d b c , H S T M Tp l m _ h s t m t ,c h a r* l o g s t r i n g ) {
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx 63/66
14/05/13
R E T C O D E p l m _ r e t c o d e=S Q L _ S U C C E S S ; U C H A R p l m _ s z S q l S t a t e [ M A X B U F L E N ]=" " , p l m _ s z E r r o r M s g [ M A X B U F L E N ]=" " ; S D W O R D p l m _ p f N a t i v e E r r o r=0 L ; S W O R D p l m _ p c b E r r o r M s g=0 ; p r i n t f ( l o g s t r i n g ) ; w h i l e( p l m _ r e t c o d e! =S Q L _ N O _ D A T A _ F O U N D ){ p l m _ r e t c o d e=S Q L E r r o r ( p l m _ h e n v ,p l m _ h d b c , p l m _ h s t m t ,p l m _ s z S q l S t a t e , & p l m _ p f N a t i v e E r r o r , p l m _ s z E r r o r M s g ,M A X B U F L E N-1 , & p l m _ p c b E r r o r M s g ) ; i f( p l m _ r e t c o d e! =S Q L _ N O _ D A T A _ F O U N D ) { p r i n t f ( " s z S q l S t a t e=% s \ n " ,p l m _ s z S q l S t a t e ) ; p r i n t f ( " p f N a t i v e E r r o r=% d \ n " , p l m _ p f N a t i v e E r r o r ) ; p r i n t f ( " s z E r r o r M s g=% s \ n " ,p l m _ s z E r r o r M s g ) ; p r i n t f ( " p c b E r r o r M s g=% d \ n \ n " , p l m _ p c b E r r o r M s g ) ; }/ / e n di f }/ /e n dw h i l e }
Getimage.c
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
/ /S a m p l er e a d i n gS Q L _ L O N G V A R B I N A R Yu s i n gS Q L G e t D a t a . / /T e s t e dw i t hS Q LS e r v e r6 . 5a n d2 . 6 5d r i v e r s . / /A s s u m e sD S Nh a st a b l e : / / C R E A T ET A B L EE M P 2( N A M EC H A R ( 3 0 ) ,A G EF L O A T , / / B i g B i nI M A G E ) # i n c l u d e< s t d i o . h > # i n c l u d e< s t r i n g . h > # i n c l u d e< w i n d o w s . h > # i n c l u d e< s q l . h > # i n c l u d e< s q l e x t . h > # d e f i n eM A X D S N 2 5 # d e f i n eM A X U I D 2 5 # d e f i n eM A X A U T H S T R 2 5 # d e f i n eM A X B U F L E N 2 5 5 # d e f i n eB U F F E R S I Z E 4 5 0 H E N V h e n v=S Q L _ N U L L _ H E N V ; H D B C h d b c 1=S Q L _ N U L L _ H D B C ; H S T M T h s t m t 1=S Q L _ N U L L _ H S T M T ; c h a r l o g s t r i n g [ M A X B U F L E N ]=" " ; v o i d P r o c e s s L o g M e s s a g e s ( H E N Vp l m _ h e n v ,H D B Cp l m _ h d b c , H S T M Tp l m _ h s t m t ,c h a r* l o g s t r i n g ) ; i n tm a i n ( ) { R E T C O D Er e t c o d e ; / /A u t h o r i z a t i o ns t r i n g s . U C H A R s z D S N [ M A X D S N + 1 ]=" a b 6 5 d e f " , s z U I D [ M A X U I D + 1 ]=" s a " , s z A u t h S t r [ M A X A U T H S T R + 1 ]=" p a s s w o r d " ; S W O R D c n t r ;
64/66
14/05/13
/ /A l l o c a t eO D B Cc o n n e c t i o na n dc o n n e c t . r e t c o d e=S Q L A l l o c C o n n e c t ( h e n v ,& h d b c 1 ) ; / /M a k et h ec o n n e c t i o n ,t h e np r i n tt h ei n f o r m a t i o nm e s s a g e s . r e t c o d e=S Q L C o n n e c t ( h d b c 1 ,s z D S N ,( S W O R D ) s t r l e n ( s z D S N ) , s z U I D ,( S W O R D ) s t r l e n ( s z U I D ) , s z A u t h S t r , ( S W O R D ) s t r l e n ( s z A u t h S t r ) ) ; i f(( r e t c o d e! =S Q L _ S U C C E S S )& & ( r e t c o d e! =S Q L _ S U C C E S S _ W I T H _ I N F O )){ P r o c e s s L o g M e s s a g e s ( h e n v ,h d b c 1 ,h s t m t 1 , " S Q L C o n n e c t ( )F a i l e d \ n \ n " ) ; r e t u r n ( 9 ) ; } e l s e{ P r o c e s s L o g M e s s a g e s ( h e n v ,h d b c 1 ,h s t m t 1 , " \ n C o n n e c tS u c c e s s f u l \ n \ n " ) ; } / /A l l o c a t et h es t a t e m e n th a n d l e . r e t c o d e=S Q L A l l o c S t m t ( h d b c 1 , & h s t m t 1 ) ; / /E x e c u t et h eS E L E C Ts t a t e m e n t . r e t c o d e=S Q L E x e c D i r e c t ( h s t m t 1 , " S E L E C TB i g B i nF R O Me m p 2 " ,S Q L _ N T S ) ; i f(( r e t c o d e! =S Q L _ S U C C E S S )& & ( r e t c o d e! =S Q L _ S U C C E S S _ W I T H _ I N F O )){ P r o c e s s L o g M e s s a g e s ( h e n v ,h d b c 1 ,h s t m t 1 , " S Q L E x e c D i r e c th s t m t 1F a i l e d \ n \ n " ) ; r e t u r n ( 9 ) ; } / /G e tf i r s tr o w . r e t c o d e=S Q L F e t c h ( h s t m t 1 ) ; i f(( r e t c o d e! =S Q L _ S U C C E S S )& & ( r e t c o d e! =S Q L _ S U C C E S S _ W I T H _ I N F O )){ P r o c e s s L o g M e s s a g e s ( h e n v ,h d b c 1 ,h s t m t 1 , " S Q L F e t c hh s t m t 1F a i l e d \ n \ n " ) ; r e t u r n ( 9 ) ; } / /G e tt h eS Q L _ L O N Gc o l u m n .C b B a t c hh a ss i z eo fd a t ac h u n k / /t h eb u f f e rc a nh a n d l e .C a l lS Q L G e t D a t au n t i l / /S Q L _ N O _ D A T A _ F O U N D . c b B i n S i z eo ne a c hc a l lh a st h e / /a m o u n to fd a t al e f tt ot r a n s f e r . c n t r=1 ; d o{ r e t c o d e=S Q L G e t D a t a ( h s t m t 1 , / /h s t m t 1 , / /i p a r S Q L _ C _ B I N A R Y , / /f C T y p e D a t a , / /r g b V a l u e c b B a t c h , / /c b V a l u e M a x & c b B i n S i z e ) ; / / p c b V a l u e msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
65/66
14/05/13
& c b B i n S i z e ) ; / /p c b V a l u e p r i n t f ( " G e t D a t ai t e r a t i o n% d ,p c b V a l u e=% d \ n " , c n t r + + ,c b B i n S i z e ) ; i f(( r e t c o d e! =S Q L _ S U C C E S S )& & ( r e t c o d e! =S Q L _ S U C C E S S _ W I T H _ I N F O )& & r e t c o d e! =S Q L _ N O _ D A T A _ F O U N D) { P r o c e s s L o g M e s s a g e s ( h e n v ,h d b c 1 ,h s t m t 1 , " S Q L G e t D a t ah s t m t 1F a i l e d \ n \ n " ) ; r e t u r n ( 9 ) ; } }w h i l e( r e t c o d e! =S Q L _ N O _ D A T A _ F O U N D ) ; / *C l e a nu p .* / S Q L F r e e S t m t ( h s t m t 1 ,S Q L _ D R O P ) ; S Q L D i s c o n n e c t ( h d b c 1 ) ; S Q L F r e e C o n n e c t( h d b c 1 ) ; S Q L F r e e E n v ( h e n v ) ; r e t u r n ( 0 ) ; }
msdn.microsoft.com/en-us/library/ms811006(d=printer).aspx
66/66