1

我正在使用 MSSQL 2008 R2,但这是一个一般的 SQL 问题。我想对我的结果进行排序只是为了保持相同的值彼此相邻,而不指定确切的排序顺序。

例如

create table t (a int not null, b int not null)

insert into t values (1, 1), (1, 2), (2, 1), (2, 2), (2, 3), (3, 1), (3, 2)

select *
from t
order by a

这将具有我想要的属性,即首先出现 a=1 的所有行,然后是 a=2,然后是 a=3。我同样可以指定 'order ba desc' 并首先获得 a=3 行。

但事实上,我会很高兴得到所有 a=2 行,然后是 a=1,然后是 a=3。

所以我上面的查询是过度指定的;当我实际上不想要该顺序时,它会向服务器询问特定的排序顺序;我只想将相同的值组合在一起。对于大表,如果服务器可以更灵活地选择返回行的顺序,则服务器可能能够更有效地查询,前提是相同的值放在一起。

是否有一些 SQL 构造,例如

   select *
   from t
   order by a indeterminate

我可以在哪里指定“您喜欢的任何顺序,只要相同的元素保持在一起”?

4

2 回答 2

1

我认为没有像您描述的那样,字段上的聚集索引通常会按索引值的顺序返回行,而没有排序依据,但不能保证。

但是,如果您有该索引,那么成本ORDER BY a将是微不足道的。

当然,如果您想随机化顺序,您可以这样做,但似乎您希望有一个性能更好的选项,而任何这样做的方法都不会表现得更好。

于 2013-07-19T15:52:52.690 回答
0

我认为这是一个有趣的问题。您正在寻找集群,但您并不真正关心集群是否有序。简短的回答是,不,没有这样的事情。

确实,对集群进行排序确实过度指定了您的要求,但是对于规模不大的问题,这是指定答案的最有效方法。让我们考虑一下 SQL Server 将如何满足您的请求。

让我们假设在第一种情况下,您的数据处于无序堆中,即没有聚集索引,并且您很少这样做。为了满足您的要求,SQL Server 可以立即返回第一行,因为您不关心顺序。但是,在它可以从第二个集群返回任何内容之前,它必须获取整个结果集以了解最后一行是否属于第一个集群。因此,在从磁盘读取所有内容之前,您几乎无法获得很多结果。

到目前为止,第一个场景非常简单,但让我们考虑一下 SQL Server 可能如何跟踪这些集群。假设您有n属于m集群的数据行。当 SQL Server 遍历您的结果时,它可以立即返回属于第一个集群的结果。但是,对于其他m-1集群,它需要将它们存储在某个地方。

SQL Server 将其索引保存在树中,因此让我们首先考虑这一点。对于m-1集群,树需要O(log(m))很深。因此,找到任何特定行所属的集群的运行时间是O(log(m))。该查询的总运行时间为O(n x log(m))

SQL Server 可以做得更好吗?它可以通过将这些索引保存在哈希中。在 a has 中找到一行的簇的时间是O(1)。因此,总运行时间为O(n)。这里的权衡是散列需要时间,一个好的散列函数很难确定,而且散列需要保留比实际需要更多的空间才能获得良好的性能。因此,对于小问题,树更快、更有效。

所以在第一种情况下,我们能做的最好的事情是O(n),一个很小但很重要的常数。

让我们考虑第二种情况,您希望在蓝月亮中多次执行此查询。你会想要一个索引。该索引将所有行保存在集群中,并且所有集群都按O(m)每次插入的成本排列。你得到什么回报?您的查询只需要从顶部(或底部)遍历索引,返回它看到的每一行。这会给你一个有序的结果。查询中不需要任何工作。我们在插入时完成了所有操作(以及更新和删除)。

所有这些都假设您的表被安排在一个磁盘上,其中访问这些数据的最有效方法是从一端到另一端遍历数据。当您跨磁盘对数据进行分区时,这不再适用。虽然我认为你应该将数据保存在内存中,但你不能总是负担得起那么多内存,所以分区很重要。

对于分区的情况,我强烈推荐一种 RAID 解决方案,这样您的所有查询都会受益,而不仅仅是这个。通过以较小的规模进行条带化,无论您的数据如何分布,您都可以获得性能。除非您碰巧获取仅属于一个磁盘的数据,否则没关系。

如果您在 RAID 无法正常工作的非对称设备上进行分区,那么也许您可以考虑将多个查询拼接在一起,每个查询恰好跨越一个分区。

于 2013-07-21T20:32:57.207 回答