我们有非常大的数据库表,我需要选择最大 id 的值。鉴于表大小 SELECT MAX(id) FROM tableA 不会表现得足够好。
表定义(为了清楚起见省略了列)
CREATE TABLE [dbo].[TableA](
[id] [numeric](19, 0) NOT NULL,
[timeSampled] [datetime] NOT NULL
CONSTRAINT [PK_TableA] PRIMARY KEY NONCLUSTERED
(
[id] DESC,
[timeSampled] DESC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
Run Code Online (Sandbox Code Playgroud)
该表在 timesampled 上按月分区,并且因为我们将分区切换到另一个表以存档数据,timeSampled 列必须包含在主键中。
所以,PK是
[id] DESC,
[timeSampled] DESC
Run Code Online (Sandbox Code Playgroud)
该表包含 2013 年 5 月和 2013 年 6 月的行,id 以日期顺序递增(即最大 id 为 6 月)
当我从表中选择前 1 个 id 时,如下所示
SELECT top 1 id
FROM dbo.TableA
WITH (INDEX (PK_TableA))
Run Code Online (Sandbox Code Playgroud)
我没有得到最大 id,而是从 May 得到最大 id(即 May 分区中的最后一个条目)。
任何想法为什么会这样?基本上似乎是从一个分区中选择 MAX id,而不是所有分区中的 max
基本上似乎是从一个分区中选择 MAX id,而不是所有分区中的 max
写入TOP (1)
没有ORDER BY
子句定义哪一行是“顶部”是指查询处理器在逻辑上是自由地返回任何从该组行。优化器选择的查询计划恰好返回一个特定的行(第一个分区的最高 id),但您不能依赖它,即使它是一个有用的结果。
每当您使用时,TOP
您应该始终ORDER BY
在相同范围内指定一个以产生确定性行为 - 除非您真的不关心返回哪些行。
鉴于表大小 SELECT MAX(id) FROM tableA 不会表现得足够好
优化器缺乏将分区索引上的标量MAX
或MIN
聚合转换为每个分区聚合上的全局聚合的逻辑。Itzik Ben-Gan 在本文中解释了限制并提供了一般解决方法。
如果已知最高分区号并保证不会更改,则使用该$partition
函数指定文字分区的解决方法将起作用,尽管如果将来分区策略发生更改,它可能会以不明显的方式失败。
这种“解决方案”的工作原理是消除除一个分区之外的所有分区,从而对索引的一个分区进行简单的查找。
由于某种原因,按 id 添加订单不会提高性能
相同的优化器限制广泛适用于TOP (1) ... ORDER BY
. 这ORDER BY
使结果具有确定性,但在这种特殊情况下无助于产生更有效的计划(但见下文)。
您的索引位于id DESC, timeSampled DESC
。在 SQL Server 2008 及更高版本中,分区引入了一个额外的隐含前导键$partition ASC
(它总是升序,它不可配置)制作完整索引键$partition ASC, id DESC, timeSampled DESC
。
由于id
和timeSampled
一起增加(尽管架构中没有任何内容可以保证这一点),您可以将查询重写为TOP (1) ... ORDER BY $partition DESC, id DESC
. 不幸的是,DESC
索引上的键和ASC
隐含的前导键$partition
意味着索引不能用于按顺序扫描索引中的一行。
如果您的索引键改为,id ASC, timeSampled ASC
则整个索引键将是$partition ASC, id ASC, timeSampled ASC
. 这个全ASC
索引可以向后扫描,只返回键顺序的第一行。该行将保证id
在编号最高的分区中具有最高值。给定 id 和分区 id 之间的(非强制)关系,这将产生正确的结果,并使用仅读取一行的最佳执行计划。
此“解决方案”缺乏完整性,因为未强制执行 id-timeSampled 关系,而且您可能无论如何都不想重建非聚集主键。尽管如此,我还是提到了它,因为它可以增强您对分区如何与索引交互的理解。
归档时间: |
|
查看次数: |
1314 次 |
最近记录: |