【问题标题】:Performant stable random sort in SQL Server?SQL Server 中性能稳定的随机排序?
【发布时间】:2018-02-11 15:24:19
【问题描述】:

我想查询一个相当大的表(数百万行),提供一个种子值,以保证随机顺序的方式 - 但只要使用相同的种子,该表在多个查询中保持稳定。

到目前为止我想出的最好的是

SELECT TOP n *
      FROM tbl t
  ORDER BY t.int_column % seed, t.int_column

无论从性能角度还是从结果行在不同种子上的稍微均匀分布的角度来看,这是一种可用的方法吗?

编辑:

对于上下文,需要稳定排序,因为多个(可能是嵌套的)WHERE NOT IN 查询对同一数据集进行操作;例如

SELECT *
  FROM tbl t
 WHERE t.some_criteria = 'some_value'
   AND t.id NOT IN
(
    SELECT TOP n t.id
          FROM tbl t
         WHERE t.some_other_criteria = 'some_other_value'
      ORDER BY t.int_column % seed, t.int_column
)
   AND t.id NOT IN
(
    # etc.
)

当子选择的顺序是随机的,但不稳定(即NEWID()TABLESAMPLE())时,结果行在执行之间波动很大。

【问题讨论】:

  • 数据在哪里消费?如果您没有将数据传递给另一个存储过程或其他数据库内代码,那么根据非平凡标准对行进行排序的行为是一个视图级别的问题,不应该在您的数据库代码中,而是在您的应用程序中代码。
  • @Dai - 好问题。请查看我的编辑。
  • 您将始终对符合条件的所有记录进行排序,这可能会很多。你确定你需要所有这些NOT IN 子句吗?也许您可以稍微简化查询。你到底想达到什么目的?
  • @ThorstenKettner 是的,很遗憾。用例是根据用户提供的标准操作的选择构建器;每个子选择代表一个“左兄弟”目标组,其结果需要从下一个目标组的池中排除(因为每个实体永远只能是一个目标组的一部分)。
  • 所以随着查询越来越大,您一次又一次地选择?对于已经包含排序键和索引的临时表,可能会出现这种情况。加上您反复设置的目标组的数字。 (类似于update top (50) #temptable set targetgroup = 1 where targetgroup is null and somecriteria = 123 order by sortkeyupdate top (50) #temptable set targetgroup = 2 where targetgroup is null and othercriteria = 456 order by sortkey 等)

标签: sql sql-server sorting random discrete-mathematics


【解决方案1】:

只是一个想法...您可以在表格中添加一个“RamdomSort”列。这样,排序顺序将是真正随机的,但在您使用新值更新表之前将保持可重复的可重复性。类似的东西......

ALTER TABLE dbo.MyTable ADD RandomSort INT NOT NULL 
CONSTRAINT df_MyTable_RandomSort DEFAULT(0);


UPDATE mt SET
    mt.RandomSort = ABS(CHECKSUM(NEWID())) % 100000 + 1
FROM
    dbo.MyTable mt;

SELECT 
    *
FROM
    dbo.MyTable mt
ORDER BY 
    mt.SomeValue;

如果情况允许,您甚至可以添加一个覆盖的非聚集索引来消除排序操作。

【讨论】:

    【解决方案2】:

    如果您想要随机排序,您可以使用HASHBYTES 和您选择的行中的一些数据来执行此操作。

    SELECT TOP 100 *
      FROM tbl t
      ORDER BY HASHBYTES('SHA1', CONCAT(STR(t.int_column), 'seed string'))
    

    现在,性能是个大问题。现代 CPU 非常快地执行 SHA1,因此这可能足以满足您的需求。

    如果您能更多地了解性能而不是“良好的随机性”,您可以使用简单的linear congruential generator 作为转换函数:

    SET ARITHABORT    OFF;
    SET ARITHIGNORE   ON;
    SET ANSI_WARNINGS OFF;
    
    SELECT TOP 100 *
      FROM tbl t
      ORDER BY ((t.int_column + seed_number) * 1103515245 + 12345)
    

    这会更快,但随机性更小。

    【讨论】:

    • 不幸的是,HASHBYTES() 路由的性能比modulo 差了大约 90%。我无法完全测试线性同余生成器性能,因为它会导致 int 溢出。
    • 没关系 int 溢出(CAST AS bigint 帮助),但 less random 有点轻描淡写;事实上,订单非常稳定;)
    • LC 的“随机性”来自整数环绕模的情况(通常这发生在您使用的任何整数类型的最大值处)。我实际上不知道如何让 SQL Server 进行包装数学。
    • 啊啊啊啊啊!我知道我错过了一些东西,只是看起来错了。真可惜,不过话说回来,已经过了凌晨 3 点。感谢您的输入,我将在 sql server 中进一步研究包装数学,如果有任何结果,我会更新您。干杯!
    • 最简单的选择是在ORDER BY 的后面加上一个% partition_count,但如果你能告诉SQL Server 你不关心溢出,它会更快。跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-03
    • 1970-01-01
    • 1970-01-01
    • 2012-07-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多