Very long query execution using C++, Sql Server and ODBC connection - c++

I'm using the following code to connect to a sql-server database. I can run all other queries with no difficulties using the same function. However, one query executes in Sql management studio and in R using RODBC in ~11 seconds but takes over an hour (at the "SQLExecDirect" function) using c++. Has anyone else had this issue and how did you resolve it?
std::string sqlQuery="[myquery]";
SQLHANDLE sqlconnectionhandle;
SQLAllocHandle(SQL_HANDLE_DBC, sqlenvhandle, &sqlconnectionhandle);
SQLCHAR retconstring[1024];
SQLDriverConnect (sqlconnectionhandle, NULL,
conn_str,
len, retconstring, 1024, NULL,SQL_DRIVER_NOPROMPT);
SQLHANDLE sqlstatementhandle;
SQLAllocHandle(SQL_HANDLE_STMT, sqlconnectionhandle, &sqlstatementhandle);
//this is where the program "hangs" for over an hour
if(SQL_SUCCESS!=SQLExecDirect(sqlstatementhandle, (SQLCHAR*)(sqlQuery.c_str()), SQL_NTS)){
show_error(SQL_HANDLE_STMT, sqlstatementhandle, errorMsg);
return;
}
int numRow=0;
while(SQLFetch(sqlstatementhandle)==SQL_SUCCESS){
for(int i=1; i<=numFields+1; ++i){
double myVal;
SQLGetData(sqlstatementhandle, i, SQL_C_DOUBLE, &myVal, 0, NULL);
cb(myVal, numRow, i-1); //callback function defined elsewhere
}
numRow++;
}
SQLFreeHandle(SQL_HANDLE_STMT, sqlstatementhandle );
SQLDisconnect(sqlconnectionhandle);
SQLFreeHandle(SQL_HANDLE_DBC, sqlconnectionhandle);

That is because the ArithAbort property on your database is off.
See also http://www.sommarskog.se/query-plan-mysteries.html
You can check it and correct it with this small script
declare #value sql_variant
select #value = SESSIONPROPERTY('ARITHABORT')
if #value <> 1
begin
USE master
ALTER DATABASE [your_database] SET ARITHABORT ON WITH NO_WAIT
use your_database
end

I figured this out: my connection string's default database was different than the one that I had as the default database in SSMS. Switching this made the query run in the same speed as SSMS.

Related

Program type out of range connecting to MS SQL

ALL,
I am trying to execute the following query:
SELECT cast(su.name AS varchar(128)) FROM sysobjects so, sysusers su, sys.tables t, sys.schemas s WHERE so.uid = su.uid AND t.object_id = so.id AND t.schema_id = s.schema_id AND s.name = ? AND so.name = ?;
against SQL Server 10.0 (as returned from SELECT SERVERPROPERTY('productversion')) and am receiving the aforementioned error.
The only google result for this error and MS SQL Server is this link, but doing what is recommended as a 2nd solution didn't work.
Code I use is as follows:
SQLSMALLINT nameBufLength, dataTypePtr, decimalDigitsPtr, isNullable;
SQLULEN columnSizePtr;
SQLLEN cbTableOwner;
retcode = SQLDescribeCol( stmt, 1, NULL, 0, &nameBufLength, &dataTypePtr, &columnSizePtr, &decimalDigitsPtr, &isNullable );
if( retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO )
{
owner = new SQLWCHAR[columnSizePtr + 1];
retcode = SQLBindCol( stmt, 1, dataTypePtr, &owner, columnSizePtr, &cbTableOwner );
if( retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO )
{
}
}
Does anybody know why am I getting the error? It's not even mentioned here.
TIA!
EDIT:
I am using SQL Server Native Client 11.0 ODBC driver version 2011.110.2100.60 from 11 Feb 2012. I also have SQL Server Native Client 10.0 installed. The driver I use should be backward compatible. But I will try with 10.0 just in case tomorrow.
EDIT2:
Trying to use older driver also didn't work. I guess I will try to workaround the error.
In SQLBindCol(), set the dataTypePtr to SQL_WCHAR instead of SQL_WVARCHAR for unicode and to SQL_CHAR instead of SQL_VARCHAR for ascii.

C++ SQLExecDirect INSERT doesn't work

I've wrote a simple SQL C++ Wrapper-Class, where I encountered a real strange Problem. When I call an INSERT-Command with SQLExecDirect, the data does not appear in the SQL Database (SQL Server 2012), although SQLRowCount returns one row. The table, which I'm trying to write to, is named "Person" and has four columns (ID (AUTOINCREMENT), Firstname (nvarchar(100)), Lastname (nvarchar(100)), Birthday (date))
Here is my code:
Class-Constructor (EnvHandle, DBCHandle and StmtHandle are class-members)
SQLDatabase::SQLDatabase()
{
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &EnvHandle);
SQLSetEnvAttr(EnvHandle, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
SQLAllocHandle(SQL_HANDLE_DBC, EnvHandle, &DBCHandle);
SQLSetConnectAttr(DBCHandle, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER);
}
Query-Member-Function
int SQLDatabase::ExecuteNonQuery(std::wstring Command)
{
SQLRETURN RetCode = -1;
SQLINTEGER RowCount = 0;
SQLAllocHandle(SQL_HANDLE_STMT, DBCHandle, &StmtHandle);
RetCode = SQLExecDirect(StmtHandle, (SQLWCHAR*)Command.c_str(), Command.length());
if (RetCode == SQL_SUCCESS || RetCode == SQL_SUCCESS_WITH_INFO)
{
SQLRowCount(StmtHandle, &RowCount);
SQLFreeHandle(SQL_HANDLE_STMT, StmtHandle);
return RowCount;
}
else
{
SQLFreeHandle(SQL_HANDLE_STMT, StmtHandle);
return -1;
}
}
Function-Call in main.cpp
wcout << "Firstname:\n>";
wcin >> Firstname;
wcout << "Lastname:\n>";
wcin >> Lastname;
wcout << "Birthday:\n>";
wcin >> Birthday;
InsRows = database.ExecuteNonQuery(L"INSERT INTO Person (Firstname,Lastname,Birthday) VALUES ('" + Firstname + L"','" + Lastname + L"','" + Birthday + L"')");
if (InsRows == -1)
database.Error();
else
std::wcout << InsRows << " rows affected!" << std::endl;
As already said, "database.ExecuteNonQuery" returns one row.
When take a look at the table in SQL Management Studio, the datarow hasn't been added. I've already traced the queries on the table. The query appears in the tracelog correctly without any additional info. My IDE is Visual Studio 2013.
Any ideas how I could get the data into the table?
Thanks!
Sebastian
In general, whenever a query is executed that changes a table (without error) and the results are not shown, this means that the changes were not committed.
Looking at your code, you have this:
SQLSetConnectAttr(DBCHandle, SQL_ATTR_AUTOCOMMIT,
(SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER);}
You set the connection attribute to have auto commit turned off. Either specify you want auto commit on, or issue a call to SQLEndTran to commit the changes.
You're using SQL_AUTOCOMMIT_OFF, which means your statements won't be automatically committed, so your new record is visible only in your transaction and will never be committed in the database. Try to use the auto-commit feature of the MS SQL Server (setting SQL_ATTR_AUTOCOMMIT to SQL_AUTOCOMMIT_ON or just leave it uninformed since it's the default behaviour) or explicitly begin and commit your transaction.
http://msdn.microsoft.com/en-us/library/ms713605(v=vs.85).aspx

SQLDescribeParams function call - SQL Server Native Client 11.0 driver fails with “Invalid object name”

I want to create a temporary table on SQL Server 2012 and insert into it in bulk. I don't always know the type of parameters that the temp table will be created with, so I need to invoke SQLDescribeParam so that I can bind the parameters. I am using the following stripped down code after copying the sample code for SQLDescribeParam from http://msdn.microsoft.com/en-us/library/ms710188(v=vs.85).aspx
The code below works perfectly fine for the "SQL Server Native Client 10.0" ODBC driver against SQL Server 2012, and I am able to bind parameters and insert into the temp table. But the SQLDescribeParam call fails for "SQL Server Native Client 11.0" driver with the following two errors:
Error record 1:
Description: '[Microsoft][SQL Server Native Client 11.0][SQL Server]Invalid object name '#myTemp'.'
SQL State: 42S02
Native Error: 208
Error record 2:
Description: '[Microsoft][SQL Server Native Client 11.0][SQL Server]The batch could not be analyzed because of compile errors.'
SQL State: 42000
Native Error: 11501
What do I need to do to get SQLDescribeParam to behave on "SQL Server Native Client 11.0"?
I am using the 64 bit "SQL Server Native Client" driver on Windows 7.
The code is below:
SQLSMALLINT NumParams, i, DataType, DecimalDigits, Nullable;
SQLULEN ParamSize;
SQLWCHAR strCreateQuery[] = L"CREATE TABLE [#myTemp] ( mycol INT)";
SQLWCHAR strInsertQuery[] = L"INSERT INTO [#myTemp] ([mycol]) VALUES (?)";
SQLHSTMT hstmt;
{ // Create temp table #myTemp
// Allocate hstmt
SQLExecDirectW( hstmt, strCreateQuery, SQL_NTS );
// Deallocate hstmt
}
// Allocate hstmt
SQLPrepare(hstmt, strInsertQuery, SQL_NTS);
SQLNumParams(hstmt, &NumParams);
if (NumParams) {
for (i = 0; i < NumParams; i++) {
// Describe the parameter.
SQLDescribeParam(hstmt, i + 1, &DataType, &ParamSize, &DecimalDigits, &Nullable) );
}
}
// Deallocate hstmt

Excute create table in SQL Server with C++

I am trying to create a table in SQL Server with following code:
#include <iostream>
#include <windows.h>
#include <string>
#include <sql.h>
#include <sqltypes.h>
#include <sqlext.h>
using namespace std;
void main()
{
SQLHANDLE sqlevent, sqlconnection, sqlstatement;
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &sqlevent);
SQLSetEnvAttr(sqlevent, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
SQLAllocHandle(SQL_HANDLE_DBC, sqlevent, &sqlconnection);
SQLCHAR retstring[10000];
SQLDriverConnect(sqlconnection, NULL, (SQLCHAR*)("DRIVER={SQL Server Native Client 10.0};SERVER=SERVER;DATABASE=DATABASE;UID=CrystalReports;PWD=PASSWORD"), SQL_NTS, retstring, 10000, NULL, SQL_DRIVER_NOPROMPT);
SQLAllocHandle(SQL_HANDLE_STMT, sqlconnection, &sqlstatement);
string commandline;
commandline = "CREATE TABLE NEW_TABLE01(Name VARCHAR(10), Age INT, Salary DOUBLE PRECISSION)";
if(SQL_SUCCESS != SQLExecDirect(sqlstatement, (SQLCHAR*)(commandline.c_str()), SQL_NTS)) // SQLExecDirect always failed to excute
{
cout<<"The create table sql command hasn't been excuted successfully."<<endl;
return;
}
return;
}
However, the code if(SQL_SUCCESS != SQLExecDirect(sqlstatement, (SQLCHAR*)(commandline.c_str()), SQL_NTS)) always return a TRUE, which means the SQL command was not sucessfully excuted.
I've got all the permission to create a table, and I've check the SQL command in SQL server manually, and it works.
May I know what am I wrong? And how can I achieve my goal?
Many thanks in advance.
I think I've got answer since inspired by C++ SQL Database program
I realised that there are different schemas in SQL Server, thus I use CREATE TABLE [ROU].[NEW_TABLE01] instead. Now my code works well.

How to write to a varchar(max) column using ODBC

Summary: I'm trying to write a text string to a column of type varchar(max) using ODBC and SQL Server 2005. It fails if the length of the string is greater than 8000. Help!
I have some C++ code that uses ODBC (SQL Native Client) to write a text string to a table. If I change the column from, say, varchar(100) to varchar(max) and try to write a string with length greater than 8000, the write fails with the following error
[Microsoft][ODBC SQL Server
Driver]String data, right truncation
So, can anyone advise me on if this can be done, and how?
Some example (not production) code that shows what I'm trying to do:
SQLHENV hEnv = NULL;
SQLRETURN iError = SQLAllocEnv(&hEnv);
HDBC hDbc = NULL;
SQLAllocConnect(hEnv, &hDbc);
const char* pszConnStr = "Driver={SQL Server};Server=127.0.0.1;Database=MyTestDB";
UCHAR szConnectOut[SQL_MAX_MESSAGE_LENGTH];
SWORD iConnectOutLen = 0;
iError = SQLDriverConnect(hDbc, NULL, (unsigned char*)pszConnStr,
SQL_NTS, szConnectOut,
(SQL_MAX_MESSAGE_LENGTH-1), &iConnectOutLen,
SQL_DRIVER_COMPLETE);
HSTMT hStmt = NULL;
iError = SQLAllocStmt(hDbc, &hStmt);
const char* pszSQL = "INSERT INTO MyTestTable (LongStr) VALUES (?)";
iError = SQLPrepare(hStmt, (SQLCHAR*)pszSQL, SQL_NTS);
char* pszBigString = AllocBigString(8001);
iError = SQLSetParam(hStmt, 1, SQL_C_CHAR, SQL_VARCHAR, 0, 0, (SQLPOINTER)pszBigString, NULL);
iError = SQLExecute(hStmt); // Returns SQL_ERROR if pszBigString len > 8000
The table MyTestTable contains a single colum defined as varchar(max). The function AllocBigString (not shown) creates a string of arbitrary length.
I understand that previous versions of SQL Server had an 8000 character limit to varchars, but not why is this happening in SQL 2005?
Thanks,
Andy
You sure you load the SQL Native Driver for 2005, not the old driver for 2000? The native driver name is {SQL Server Native Client 10.0} for 2k8 or {SQL Native Client} for 2k5
The error message ODBC SQL Server Driver seem to indicate the old 2k driver (I may be wrong, haven't touch ODBC in like 10 years now).
Turns out that although the fix works for SQLSetParam, it does not work for SQLBindParameter.
For example:
int iLength = 18001;
char* pszBigString = new char[iLength + 1];
memset(pszBigString, 'a', iLength);
pszBigString[iLength] = 0;
LONG_PTR lLength = SQL_NTS;
::SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT,
SQL_C_CHAR,
SQL_VARCHAR,
iLength, 0, pszBigString, iLength * sizeof(TCHAR),
&lLength);
will result in the same 22001 "String data, right truncation" error, regardless of which driver is used.
In fact, my experiments have shown that you do not actually need to install version 10 of the client driver. Instead you should use SQL_LONGVARCHAR instead of SQL_VARCHAR if you expect the lengths of your strings to exceed 8000 characters. You could potentially perform a mass find-and-replace, but it's possible that using SQL_LONGVARCHAR might incur some sort of penalty (although that's pure speculation; it's an 'extended datatype').
I have tested this successfully with both drivers on Windows XP:
{SQL Server} 2000.85.1117.00 (04/08/2004)
{SQL Server Native Client 10.0} 2007.100.1600.22 (10/07/2008)