【问题标题】:Selecting n random rows from a huge database with conditions从具有条件的巨大数据库中选择 n 个随机行
【发布时间】:2013-11-14 21:22:13
【问题描述】:

我有一个大约 800 万行的数据库,我想从中随机选择 n 行。首先我在StackOverflowMSDN 上阅读了流行和类似的问题,但是我觉得答案仍然不适合我的需要。

如果我希望在没有额外条件的情况下随机选择一定百分比的行,所提供的解决方案非常有用。但是我想随机选择n行(例如最多5行),都符合一定的条件。

我的数据库包含带有词性、标签、引理和标记等信息的单词。现在我想执行一个查询来选择 5 个与查询中的单词相似的随机词(例如,给我 5 个类似于 fuzzy 的词),这是通过只查看具有相同部分的词来确定的语音和 levenshtein 距离的值高于某个阈值。我在 sql server 中有一个函数可以计算 levenshtein 距离。

上述方法的问题在于,它们要么必须遍历所有记录并计算 levenshtein 距离(这会占用大量时间!),要么只让我选择一个百分比而不是 n 行。

一个运行良好的查询是:

SELECT DISTINCT TOP 5 lower(Words.TOKEN) as LTOKEN, Words.LEMMA, TagSet.POS_Simplified, TagSet.TAG 
FROM Words JOIN TagSet on Words.TAG = TagSet.TAG 
WHERE NOT Words.LEMMA = 'monarchie' AND TagSet.POS_Simplified = 'noun' 
AND TagSet.TAG = 'NOM' AND NOT Words.TOKEN = 'monarchie'
AND [dbo].edit_distance('monarchie', Words.Token) > 0.5

但是,只有 top 我总是得到相同的结果。我需要我的上衣是随机的。使用 NEWID() 之类的方法将首先遍历整个数据库,然后随机选择,这不是我的预期行为,因为它们花费的时间太长。

有没有人想在一个庞大的数据库上快速选择 n 个随机行?


编辑:

某人(不在 StackOverflow 上)可能为我提供了一个带有 OPTION 子句和 fast 关键字的解决方案,该关键字检索它找到的前 n 行。

使用 OPTION(fast 5) 我获得了迄今为止最好的性能(在超过 800 万行的表上需要 10 秒)。我还将 Levenshtein 函数从 SQL 实现更改为 c# 编写的库实现,这大大提高了性能。

Select top 5 * from (
SELECT DISTINCT lower(Words.TOKEN) as LTOKEN, Words.LEMMA, TagSet.POS_Simplified, TagSet.TAG 
FROM Words JOIN TagSet on Words.TAG = TagSet.TAG 
WHERE NOT Words.LEMMA = 'monarchie' AND TagSet.POS_Simplified = 'noun' 
AND TagSet.TAG = 'NOM' AND NOT Words.TOKEN = 'monarchie'
AND [dbo].clrEditDistance('monarchie', Words.Token) > 0.5
) AS T
ORDER BY NEWID()
OPTION(fast 5)

【问题讨论】:

  • 添加到您的查询末尾:ORDER BY NEWID() 排序操作在查询执行中最后。或者试试这个查询:SELECT * FROM () AS T ORDER BY NEWID()
  • 除了您问题的随机性方面,使用 SQLServer 的内置全文功能(CONTAINS 等)不是更好吗?
  • @realnumber3012 你的第一个建议仍然会检查我的所有记录,大约需要 5 分钟才能完成(当我为每条记录而不是少数几个记录运行查询时,这基本上是相同的性能) .您的第二个建议只会随机化我查询中的 5 个结果行,这不是我想要的。我想要表中的 5 个随机行,而不是前 5 行的随机顺序。
  • @davek 老实说,我没有意识到 SQLServer 的 CONTAINS 函数的全部功能。我不确定它是否符合我的需求,但我一定会研究一下。
  • TOP N ORDER_BY NEWID() 不只是随机化前 N 个的顺序;它隐式地为每一行分配一个 NEWID,并且由于 NEWID 分布均匀,这将为您提供一个随机(嗯,伪随机)的 N 行集合。从随机性的角度来看,我担心这个“OPTION(fast 5)”;在我看来,允许 SQL Server 选择一个方便的排序来避免 ORDER BY NEWID() 给你的伪随机排序。

标签: sql sql-server performance random query-performance


【解决方案1】:

避免全面扫描将很困难。如果您有一个可以轻松随机选择的列,例如,您碰巧有一个“密集”的身份列,并且间隙很少,则将 Klark 的方法替换为以下修改:

declare @results table (id bigint, name varchar(100))

while (select count(*) from @results) < 5
    begin
    insert  @results
            (name)
    select  name
    from    (
            select  *
            from    dbo.Words
            WHERE  IDCOLUMN = CONVERT(INT,RAND()) * APPX_NUMBER_OF_ROWS
            ) as SubQueryAlias          
    where   dbo.edit_distance(left(name,4), 'APRS', 100) < 3
    end  

select  *
from    @results)

【讨论】:

    【解决方案2】:

    我认为你能以多快的速度做你正在寻找的事情是有一个基本限制的。如果您想快速从表中挑选记录,您需要一种使用索引的方法。假设您有一个连续的整数列 ID,它是聚集索引:您可以选择具有不同随机 ID 值的记录,但您不能保证 MIN(ID) 和 MAX(ID) 之间的每个 ID 都会在表,因此您最终可能会得到比您要求的更少的行。

    您可能会做的一件事是获取具有您想要应用的条件的查询并添加一个 row_number(请参阅this technet article),从而为您提供一个没有“孔”的顺序“键”...选择随机该键范围内的整数。然后,您将处理表格的一个子集而不是整个表格,但我怀疑这可能是您在性能方面可以做到的最好的。

    您可以通过编写一个使用循环的表值函数来处理 ID 中的“漏洞”(只需继续选择随机值,直到获得所需的结果数量),但这并不优雅,并且可能会出现并发问题,具体取决于访问数据库上的模式。所以这取决于你在这些方面的要求。

    【讨论】:

      【解决方案3】:

      根据您的问题,我假设您知道许多行将符合您的 edit_distance &gt; 0.5 条件。但 SQL Server 不知道这一点。与 SQL Server 共享该信息的一种方法是使用表变量编写更明确的查询。

      declare @results table (id bigint, name varchar(100))
      
      while (select count(*) from @results) < 5
          begin
          insert  @results
                  (name)
          select  name
          from    (
                  select  top 100 *
                  from    dbo.Words
                  order by
                          newid()
                  ) as SubQueryAlias          
          where   dbo.edit_distance(left(name,4), 'APRS', 100) < 3
          end  
      
      select  top 5 *
      from    @results
      

      上面的 sn-p 一次选择 100 个随机行,并将匹配的行插入到结果表中。它循环直到找到 5 行。最后,它从结果表中选择 5 行。

      如果您有很多匹配的行,这应该会更有效,但如果匹配的行很少,则效率会低得多。

      【讨论】:

      • 我必须承认我没有使用 SQL Server 的经验,而且我对您提出解决方案的方式也不是很熟悉。为什么第二个select语句也选择name?
      • 第二个选择查询提供要插入@results 的新行。它必须匹配插入列表。在我的例子中,插入列表是(name),所以查询只选择name
      【解决方案4】:

      为了获取随机数据,您需要遍历与 where 子句匹配的所有行。搜索将仅在与您的 where 表达式匹配的行上完成,因此它不会是完整的表搜索。如果您有很多与您的搜索匹配的记录,您可以执行以下操作:

      select top 5 * from
      (
      SELECT DISTINCT TOP 1000 lower(Words.TOKEN) as LTOKEN, Words.LEMMA, TagSet.POS_Simplified, TagSet.TAG 
      FROM Words JOIN TagSet on Words.TAG = TagSet.TAG 
      WHERE NOT Words.LEMMA = 'monarchie' AND TagSet.POS_Simplified = 'noun' 
      AND TagSet.TAG = 'NOM' AND NOT Words.TOKEN = 'monarchie'
      AND [dbo].edit_distance('monarchie', Words.Token) > 0.5
      ) order by newid();
      

      当然,这不会是真正随机的。

      【讨论】:

      • 这是一个可行的解决方案,但我正在寻找真正随机的东西。虽然我意识到这可能会影响性能。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-18
      • 2020-07-11
      • 1970-01-01
      • 2021-11-12
      • 1970-01-01
      • 2021-12-03
      相关资源
      最近更新 更多