Microsoft SQL Server(MSSQL)'order by'将值保持在一起,但不指定顺序(部分顺序)

时间:2013-07-19 15:46:43

标签: sql sql-server-2008-r2 sql-order-by

我使用的是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 b a desc'并首先获得a = 3行。

但事实上,我会很乐意得到所有a = 2行,然后a = 1,然后a = 3.

所以我上面的查询过度指定;当我实际上不想要那个订单时,它会要求服务器提供特定的排序顺序;我只想将相同的值组合在一起。使用大型表,如果服务器可以更灵活地选择返回行的顺序,则可以更有效地进行查询,但要求相同的值在一起。

是否有一些SQL构造,例如

   select *
   from t
   order by a indeterminate

我可以指定'你喜欢的任何顺序,只要相同的元素保持在一起'?

2 个答案:

答案 0 :(得分:1)

我认为没有像你描述的那样,字段上的聚簇索引通常会按索引值的顺序返回行,而不是顺序,但是无法保证。

但是,如果您有该索引,则ORDER BY a的费用将是微不足道的。

当然,如果你想按顺序随机化你可以做到这一点,但似乎你希望有一个更好的表现选项,任何做法的方法都不会有更好的表现。

答案 1 :(得分: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可以做得更好吗?它可以通过将这些索引保持为哈希值。在has中查找行的簇的时间是O(1)。因此,总运行时间为O(n)。这里的权衡是散列需要时间,很难确定好的散列函数,并且散列需要保留比实际需要的更好的空间以获得良好的性能。因此,对于小问题规模,树木更快,更有效。

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

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

所有这些都假定您的表被安排在一个磁盘上,访问此数据的最有效方法是从一端到另一端运行数据。在磁盘上对数据进行分区时,情况就不再如此。虽然我认为你应该把你的数据保存在内存中,但是你不能总是负担那么多的内存,所以分区很重要。

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

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