我有一个C / C ++ DLL,它连接到SQL并在循环中快速发出大量ODBC查询。唯一的事情是,从ODBC DLL开始,比从Management Studio中的T-SQL运行查询要慢得多。许多数量级更慢。
起初我以为它可能是查询本身,但后来我把它剥离成一个简单的"选择NULL"并且仍然得到相同的结果。
我想知道这是否是预期的,或者是否有一些我错过或错误的ODBC设置?
首先,我这样连接(为简洁起见,我省略了所有错误检查,但是,retcode在所有情况下都返回SQL_SUCCESS):
char *connString = "Driver={SQL Server};Server=.\\ENT2012;uid=myuser;pwd=mypwd";
...
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_UINTEGER);
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (void*)5, 0);
retcode = SQLDriverConnect(
hdbc,
0,
(SQLTCHAR*) connString,
SQL_NTS,
connStringOut,
MAX_PATH,
(SQLSMALLINT*)&connLen,
SQL_DRIVER_COMPLETE);
然后我准备语句,绑定一个参数(在本例中未使用),并绑定一个这样的列:
char queryString = "select NULL;";
SQLLEN g_int32 = 4;
SQLLEN bytesRead = 0;
...
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt)
retcode = SQLPrepare(hstmt, queryString, SQL_NTS);
retcode = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT,
SQL_C_LONG, SQL_INTEGER, sizeof(int), 0, spid, 0, (SQLLEN*)&g_int32))
retcode = SQLBindCol(hstmt, 1, SQL_C_CHAR, col_1, 32, &bytesRead);
最后,我在这样的循环中重复调用查询(例如,10000次):
retcode = SQLExecute(hstmt);
retcode = SQLFetch(hstmt);
SQLCloseCursor(hstmt);
在ODBC DLL中运行10000次大约需要90秒。在运行SQL 2012 x64的4核Windows 2008 R2服务器上进行测试。
另一方面,如果我运行,看起来像是在Management Studio中的等效测试,它只需不到一秒钟:
declare @sql varchar(128), @repeat int;
set @repeat = 10000;
set @sql = 'select NULL;';
while @repeat > 0 begin
exec(@sql);
set @repeat = @repeat - 1;
end;
有人能指出我忽略的东西吗?我的逻辑存在一些缺陷吗?
感谢。
Neil Weicher
答案 0 :(得分:0)
这对评论来说太长了
declare @sql varchar(128), @repeat int;
set @repeat = 10000;
set @sql = 'select NULL;';
while @repeat > 0 begin
exec(@sql);
set @repeat = @repeat - 1;
end;
实际上不会模拟10000次远程调用。 exec
绕过设置请求的内部的 lot 。要模拟10000次呼叫,请在SSMS中执行此操作:
select NULL;
go 10000
并衡量。可能应该使用文本输出来避免SSMS网格显示周围的时间。
答案 1 :(得分:0)
我不太熟悉T-SQL的东西,但这里有几点需要考虑。
您的ODBC驱动程序必须通过您的网络传输数据,我怀疑T-SQL执行没有。在磁盘IO旁边,在网络上传输数据是ODBC驱动程序必须执行的最慢的事情之一。您可能会发现驱动程序花费了大量时间或者等待数据传输到网络中清除数据。
另外,我不清楚你的T-SQL示例实际上是移动和数据,但是你的ODBC示例在调用SQLFetch时会这样做。 T-SQL可能只是执行查询而从不提取任何数据。因此,从循环中删除SQLFetch可能是一个更平等的比较。
要查看数据传输是否是您的限制因素,估计您使用ODBC获取的所有记录中将包含多少数据,并尝试使用类似FTP的方式在两台计算机之间移动那么多数据。 ODBC驱动程序永远不能比简单的原始数据传输更快地获取数据。我看到你只是在结果集中获取NULL,但驱动程序和数据库仍在它们之间传输数据以服务此请求。每次执行可以是几百个字节\ fetch。
答案 2 :(得分:0)
我遇到了同样的问题。我通过将ODBC驱动程序(通过ODBC管理器工具)的DSN的“光标默认模式”设置从“READ_ONLY”更改为“READ_ONLY_STREAMING”来解决此问题。仅这一点就可以使用Java 32位将我的应用程序(查询数据并将其写入文件)的速度从260秒提高到51秒,使用C ++从1234秒提高到11秒。 请参阅此帖子:http://www.etl-tools.com/forum/visual-importer/1587-question-about-data-transformation-memory-usage?start=6