SQL Server 2008 日期时间索引性能错误

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:

https://connect.microsoft.com/SQLServer/feedback/details/767196/order-by-datetime-in-odbc-fails-for-clean-sql-statements#details

我还应该注意到,我们确实查找了功能和非功能查询的查询计划。它们都根据执行计数被适当地重用。刷新缓存的计划并重新运行不会改变查询的成功。

Rem*_*anu 6

没有什么神秘的,你会得到一个好的(呃)或(非常)坏的计划,基本上是随机的,因为没有明确的索引可供使用的选择。虽然强制使用 ORDER BY 子句并因此避免排序,但日期时间列上的非聚集索引对于此查询来说是一个非常糟糕的选择。为这个查询创建一个更好的索引是 one on (serial_number, test_date)。更好的是,这将成为聚集索引键的一个很好的候选者。

根据经验,时间序列应该按时间列进行聚类,因为绝大多数请求都对特定时间范围感兴趣。如果数据也固有​​地在具有低选择性的列上进行分区,就像您的 serial_number 的情况一样,则应将此列添加为聚簇键定义中最左侧的列。

  • 这也不能解释为什么在查询的开头添加注释会影响运行持续时间。 (5认同)