このクエリを実行すると
_select top 100 * from mytable order by newid()
_
テーブルから100個のランダムなレコードを取得します。
ただし、select
リストにnewid()
が表示されないため、その動作について少し混乱しています。誰か説明できますか? newid()
について何か特別なものがありますか?
私はNewID()が何をするかを知っています。ランダム選択でどのように役立つかを理解しようとしています。 (1)selectステートメントはmytableからEVERYTHINGを選択し、(2)選択された各行に対して、NewID()によって生成されたuniqueidentifierを追加します。(3)このuniqueidentifierで行をソートし、(4)一番上を選択しますソート済みリストから100
はい。これはかなり正確です(ただし、必ずしも行をソートする必要はありませんall)。これを確認するには、実際の実行計画を確認します。
_SELECT TOP 100 *
FROM master..spt_values
ORDER BY NEWID()
_
計算スカラー演算子は、各行にNEWID()
列を追加し(クエリの例では表の2506)、表の行は選択された上位100でこの列によってソートされます。
SQL Serverは実際にセット全体を位置100からソートする必要がないため、_TOP N
_ソート演算子を使用して、メモリ内でソート操作全体を実行しようとします( 小さい値のN
)
一般的には次のように動作します:
ここで重要なのはNEWID関数です。この関数は、各行のメモリにグローバル一意識別子(GUID)を生成します。定義により、GUIDは一意であり、かなりランダムです。そのため、GUIDでORDER BY句を使用して並べ替えると、テーブル内の行:上位10パーセント(または任意のパーセンテージ)を取得すると、テーブル内の行のランダムサンプリングが行われます。
NEWIDクエリが提案されています。シンプルで、小さなテーブルに非常に適しています。ただし、大きなテーブルに使用する場合、NEWIDクエリには大きな欠点があります。 ORDER BY句により、テーブル内のすべての行がtempdbデータベースにコピーされ、そこで並べ替えられます。これにより、2つの問題が発生します。通常、ソート操作にはコストがかかります。ソートは大量のディスクI/Oを使用する可能性があり、長時間実行できます。最悪のシナリオでは、tempdbの領域が不足する可能性があります。最良のシナリオでは、tempdbは大量のディスク領域を占有する可能性がありますが、手動で縮小コマンドを実行しなければ、ディスク領域を再利用することはできません。必要なのは、tempdbを使用せず、テーブルが大きくなってもそれほど遅くならない行をランダムに選択する方法です。これを行う方法の新しいアイデアを次に示します。
SELECT * FROM master..spt_values
WHERE (ABS(CAST(
(BINARY_CHECKSUM(*) *
Rand()) as int)) % 100) < 10
このクエリの背後にある基本的な考え方は、テーブル内の各行に対して0から99の間の乱数を生成し、その乱数が指定されたパーセントの値より小さいすべての行を選択することです。この例では、行の約10%がランダムに選択されます。したがって、乱数が10未満のすべての行を選択します。
select top 100 randid = newid(), * from mytable order by randid
を使用すると、明確になります。