SQL语句在ODBC中比在T-SQL中慢得多

SQL statement much slower from ODBC than in T-SQL

本文关键字:T-SQL ODBC SQL 语句      更新时间:2023-10-16

我有一个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秒。在4核Windows 2008 R2服务器上运行SQL 2012 x64.

另一方面,如果我在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

www.netlib.com

这个注释太长了

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绕过一个批次的内部建立一个请求。要在SSMS中模拟10000次调用,请执行以下操作:

select NULL;
go 10000

和措施。输出为文本可能应该使用,以避免围绕SSMS网格显示计时。

我不是很熟悉T-SQL的东西,但这里有一些事情需要考虑。

您的ODBC驱动程序必须通过您的网络传输数据,我怀疑T-SQL执行没有。除了磁盘IO之外,在网络上传输数据是ODBC驱动程序必须做的最慢的事情之一。您可能会发现驱动程序花费了相当多的时间等待数据传输或从线路上清除数据。

另外,我不清楚你的T-SQL示例实际上移动和数据,但你的ODBC示例在调用SQLFetch时确实移动和数据。T-SQL可能只是执行查询而不获取任何数据。因此,将SQLFetch从循环中移除可能是一个更公平的比较。

要查看数据传输是否是您的限制因素,请估计使用ODBC获取的所有记录中将包含多少数据,并尝试使用FTP之类的东西在两台机器之间移动这些数据。ODBC驱动程序获取数据的速度永远不会超过简单的原始数据传输。我看到你只是在你的结果集中抓取NULL所以不是很多,但是驱动程序和数据库仍然在它们之间传输数据以服务于此请求。每次执行可能需要几百字节fetch

我也面临同样的问题。我通过将ODBC驱动程序的DSN的"游标默认模式"设置(通过ODBC管理员工具)从"READ_ONLY"更改为"READ_ONLY_STREAMING"来解决它。仅这一点就将我的应用程序(查询数据并将其写入文件)的速度从使用32位Java的260秒提高到51秒,使用c++的从1234秒提高到11秒。请看这篇文章:http://www.etl-tools.com/forum/visual-importer/1587-question-about-data-transformation-memory-usage?start=6