8

私はランダムに n 行を選択したい約 800 万以上の行のデータベースを持っています。まず、StackOverflowで人気のある同様の質問とMSDNの記事を読みましたが、答えがまだ私のニーズに合わないと感じています。

追加の条件なしでランダムに選択された特定の割合の行が必要な場合、提供されたソリューションはうまく機能します。しかし、特定の条件に一致するn行をランダムに(たとえば、最大5行)選択したいのです。

私のデータベースには、品詞、タグ、補題、トークンなどの情報を含む単語が含まれています。ここで、クエリ内の単語にすべて類似した 5 つのランダムな単語を選択するクエリを実行したいと考えています (たとえば、fuzzyに類似した 5 つの単語を教えてください)。これは、同じ品詞と値を持つ単語のみを調べることによって決定されます。特定のしきい値を超えるレーベンシュタイン距離。レーベンシュタイン距離を計算できる関数がSQLサーバーにあります。

前述の方法の問題は、すべてのレコードを実行してレーベンシュタイン距離を計算する必要があるか (これには多くの時間がかかります!)、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

ただし、トップのみを使用すると、常に同じ結果が得られます。トップをランダムにする必要があります。NEWID() を使用するようなメソッドは、最初にデータベース全体を調べてからランダムに選択しますが、これは時間がかかりすぎるため、私の意図した動作ではありません。

巨大なデータベースで n 個のランダムな行を高速に選択する考えがある人はいますか?


編集:

誰か (StackOverflow ではない) が、 OPTION句とfastキーワードを使用して解決策を提供してくれた可能性があります。これは、見つかった最初の n 行を取得します。

OPTION(fast 5) を使用すると、これまでで最高のパフォーマンスが得られます (800 万行以上のテーブルで 10 秒)。また、レーベンシュタイン関数を SQL 実装から ac# で記述されたライブラリ実装に変更したことで、パフォーマンスが大幅に高速化されました。

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)
4

4 に答える 4

1

フルスキャンを回避することは困難です。簡単にランダムに選択できる列がある場合、たとえば、ギャップがほとんどない「密な」ID 列があった場合、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)
于 2014-01-21T02:42:55.187 に答える
0

あなたの質問から、多くの行があなたのedit_distance > 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

上記のスニペットは、一度に 100 行をランダムに選択し、一致する行を結果テーブルに挿入します。5 行が見つかるまでループします。最後に、結果テーブルから 5 行を選択します。

これは、一致する行が多数ある場合はより効率的ですが、一致する行が少ない場合は効率が大幅に低下します。

于 2013-11-04T10:03:25.693 に答える