DBt*_*DBA 11 sql-server-2008 sql-server
我们正在使用 SQL Server 2008 R2,并且有一个非常大(100M+ 行)的表,其中包含一个主 id 索引和一个datetime包含非聚集索引的列。我们看到了一些非常不寻常的客户端/服务器行为,这些行为基于专门针对索引日期时间列的order by子句的使用。
我通读了以下帖子:https : //stackoverflow.com/questions/1716798/sql-server-2008-ordering-by-datetime-is-too-slow 但客户端/服务器上发生的事情比实际发生的更多这里开始描述。
如果我们运行以下查询(编辑以保护某些内容):
select *
from [big table]
where serial_number = [some number]
order by test_date desc
Run Code Online (Sandbox Code Playgroud)
每次查询都会超时。在 SQL Server Profiler 中,对服务器执行的查询如下所示:
exec sp_cursorprepexec @p1 output,@p2 output,NULL,N'select * .....
Run Code Online (Sandbox Code Playgroud)
现在,如果您将查询修改为:
declare @temp int;
select * from [big table]
where serial_number = [some number]
order by test_date desc
Run Code Online (Sandbox Code Playgroud)
SQL Server Profiler 向服务器显示执行的查询如下所示,它立即工作:
exec sp_prepexec @p1 output, NULL, N'declare @temp int;select * from .....
Run Code Online (Sandbox Code Playgroud)
事实上,您甚至可以使用空注释 ('--;') 代替未使用的声明语句并获得相同的结果。所以最初我们指出 sp 预处理器是这个问题的根本原因,但如果你这样做:
select *
from [big table]
where serial_number = [some number]
order by Cast(test_date as smalldatetime) desc
Run Code Online (Sandbox Code Playgroud)
它也可以立即工作(您可以将其转换为任何其他datetime类型),以毫秒为单位返回结果。分析器将向服务器的请求显示为:
exec sp_cursorprepexec @p1 output, @p2 output, NULL, N'select * from .....
Run Code Online (Sandbox Code Playgroud)
因此,这在某种程度上将sp_cursorprepexec程序排除在问题的全部原因之外。除此之外,sp_cursorprepexec当没有使用“order by”时也会调用 ,并且结果也会立即返回。
我们已经为这个问题搜索了很多,我看到其他人发布了类似的问题,但没有一个将其分解到这个级别。
那么其他人是否目睹了这种行为?有没有人有比在 select 语句前放置无意义的 SQL 来改变行为更好的解决方案?由于 SQL Server 应该在收集数据后调用 order by,因此这似乎是服务器中存在已久的错误。我们发现这种行为在我们的许多大表中都是一致的,并且是可重现的。
编辑:
我还应该添加一个forceseekin 也可以使问题消失。
我应该补充以帮助搜索者,抛出的ODBC超时错误是:[Microsoft][ODBC SQL Server Driver]Operation cancelled
2012 年 10 月 12 日添加:仍在寻找根本原因(同时构建了一个示例以提供给 Microsoft,我将在提交后在此处交叉发布任何结果)。我一直在研究工作查询(带有添加的注释/声明语句)和非工作查询之间的 ODBC 跟踪文件。下面发布了基本的跟踪差异。在所有 SQLBindCol 讨论完成后,它发生在对 SQLExtendedFetch 调用的调用中。调用失败并返回代码 -1,然后父线程进入 SQLCancel。由于我们能够使用 Native Client 和 Legacy ODBC 驱动程序生成此文件,因此我仍然指出服务器端的一些兼容性问题。
(clip)
MSSQLODBCTester 1664-1718 EXIT SQLBindCol with return code 0 (SQL_SUCCESS)
HSTMT 0x001EEA10
UWORD 16
SWORD 1 <SQL_C_CHAR>
PTR 0x03259030
SQLLEN 51
SQLLEN * 0x0326B820 (0)
MSSQLODBCTester 1664-1718 ENTER SQLExtendedFetch
HSTMT 0x001EEA10
UWORD 1 <SQL_FETCH_NEXT>
SQLLEN 1
SQLULEN * 0x032677C4
UWORD * 0x032679B0
MSSQLODBCTester 1664-1fd0 ENTER SQLCancel
HSTMT 0x001EEA10
MSSQLODBCTester 1664-1718 EXIT SQLExtendedFetch with return code -1 (SQL_ERROR)
HSTMT 0x001EEA10
UWORD 1 <SQL_FETCH_NEXT>
SQLLEN 1
SQLULEN * 0x032677C4
UWORD * 0x032679B0
DIAG [S1008] [Microsoft][ODBC SQL Server Driver]Operation canceled (0)
MSSQLODBCTester 1664-1fd0 EXIT SQLCancel with return code 0 (SQL_SUCCESS)
HSTMT 0x001EEA10
MSSQLODBCTester 1664-1718 ENTER SQLErrorW
HENV 0x001E7238
HDBC 0x001E7B30
HSTMT 0x001EEA10
WCHAR * 0x08BFFC5C
SDWORD * 0x08BFFF08
WCHAR * 0x08BFF85C
SWORD 511
SWORD * 0x08BFFEE6
MSSQLODBCTester 1664-1718 EXIT SQLErrorW with return code 0 (SQL_SUCCESS)
HENV 0x001E7238
HDBC 0x001E7B30
HSTMT 0x001EEA10
WCHAR * 0x08BFFC5C [ 5] "S1008"
SDWORD * 0x08BFFF08 (0)
WCHAR * 0x08BFF85C [ 53] "[Microsoft][ODBC SQL Server Driver]Operation canceled"
SWORD 511
SWORD * 0x08BFFEE6 (53)
MSSQLODBCTester 1664-1718 ENTER SQLErrorW
HENV 0x001E7238
HDBC 0x001E7B30
HSTMT 0x001EEA10
WCHAR * 0x08BFFC5C
SDWORD * 0x08BFFF08
WCHAR * 0x08BFF85C
SWORD 511
SWORD * 0x08BFFEE6
MSSQLODBCTester 1664-1718 EXIT SQLErrorW with return code 100 (SQL_NO_DATA_FOUND)
HENV 0x001E7238
HDBC 0x001E7B30
HSTMT 0x001EEA10
WCHAR * 0x08BFFC5C
SDWORD * 0x08BFFF08
WCHAR * 0x08BFF85C
SWORD 511
SWORD * 0x08BFFEE6
(clip)
Run Code Online (Sandbox Code Playgroud)
添加了 Microsoft Connect 案例 10/12/2012:
我还应该注意到,我们确实查找了功能和非功能查询的查询计划。它们都根据执行计数被适当地重用。刷新缓存的计划并重新运行不会改变查询的成功。
没有什么神秘的,你会得到一个好的(呃)或(非常)坏的计划,基本上是随机的,因为没有明确的索引可供使用的选择。虽然强制使用 ORDER BY 子句并因此避免排序,但日期时间列上的非聚集索引对于此查询来说是一个非常糟糕的选择。为这个查询创建一个更好的索引是 one on (serial_number, test_date)。更好的是,这将成为聚集索引键的一个很好的候选者。
根据经验,时间序列应该按时间列进行聚类,因为绝大多数请求都对特定时间范围感兴趣。如果数据也固有地在具有低选择性的列上进行分区,就像您的 serial_number 的情况一样,则应将此列添加为聚簇键定义中最左侧的列。