数値表が「貴重な」のはなぜですか?


112

私たちの常駐データベースの専門家は、数値表は非常に貴重であると言っています。理由はよくわかりません。これが数字の表です。

USE Model
GO

CREATE TABLE Numbers
(
    Number INT NOT NULL,
    CONSTRAINT PK_Numbers 
        PRIMARY KEY CLUSTERED (Number)
        WITH FILLFACTOR = 100
)

INSERT INTO Numbers
SELECT
    (a.Number * 256) + b.Number AS Number
FROM 
    (
        SELECT number
        FROM master..spt_values
        WHERE 
            type = 'P'
            AND number <= 255
    ) a (Number),
    (
        SELECT number
        FROM master..spt_values
        WHERE 
            type = 'P'
            AND number <= 255
    ) b (Number)
GO

ブログ投稿ごとに、与えられた根拠は

数値表は非常に貴重です。文字列の操作、ウィンドウ関数のシミュレーション、テストテーブルへの大量のデータの挿入、カーソルロジック、およびそれらなしでは信じられないほど困難な他の多くのタスクに常に使用します。

しかし、これらの用途が何であるかを正確に理解していません-「数値テーブル」がSQL Serverで大量の作業を節約する場所の説得力のある具体的な例を提供できますか?


3
数字テーブルの多くのユースケースは、必要な数字をその場で生成する再帰的CTEでも同様に満たすことができます。ただし、CTEアプローチにはパフォーマンスの低下とその他の制限があります。
ニックチャマス

4
@Nick:CTEベースのオンザフライの数値テーブルと物理テーブルは、数値テーブルを生成する方法の実装の詳細にすぎません。ポテト対ポテト...
レムスRusanu

1
@Remus-うん。ジェフのこの代替案を指摘したかっただけです。
ニックチャマス

2
SO stackoverflow.com/search?q=user%3A27535+%2B%22numbers+table%22の数値表を使用して、多数の回答があります。
gbn

回答:


82

「欠損データ」を投影する必要がある場合、多くの用途を見てきました。例えば。時系列(たとえばアクセスログ)があり、過去30日間の1日あたりのヒット数を表示する(分析ダッシュボードを考える)。を実行するselect count(...) from ... group by dayと、毎日のカウントが取得されますが、結果には、実際に少なくとも1つのアクセスがあった各日の行のみが含まれます。一方、最初に数値テーブル(select dateadd(day, -number, today) as day from numbers)から日数のテーブルを投影し、次にカウント(または外部適用、好きなもの)と結合したままにしておくと、その日のカウントが0になる結果が得られますアクセスできませんでした。これはほんの一例です。もちろん、ダッシュボードのプレゼンテーションレイヤーが行方不明の日を処理し、代わりに0のみを表示できると主張するかもしれませんが、一部のツール(SSRSなど)ではこれを処理できません。

私が見た他の例では、同様の時系列トリック(日付/時刻+/-数値)を使用して、あらゆる種類のウィンドウ計算を行いました。一般に、命令型言語でよく知られている反復回数のforループを使用する場合はいつでも、SQLの宣言的性質および集合的性質は、数値テーブルに基づくトリックを使用できます。

ところで、私は、数値テーブルを使用して、それ命令的な手続き実行のように感じても、それ命令的であると仮定するという誤りに陥らないという事実を呼び出す必要があると感じています。例を挙げましょう:

int x;
for (int i=0;i<1000000;++i)
  x = i;
printf("%d",x);

このプログラムは999999を出力しますが、これはほぼ保証されています。

数値テーブルを使用して、SQL Serverで同じことを試してみましょう。最初に1,000,000個の数字のテーブルを作成します。

create table numbers (number int not null primary key);
go

declare @i int = 0
    , @j int = 0;

set nocount on;
begin transaction
while @i < 1000
begin
    set @j = 0;
    while @j < 1000
    begin
        insert into numbers (number) 
            values (@j*1000+@i);
        set @j += 1;
    end
    commit;
    raiserror (N'Inserted %d*1000', 0, 0, @i)
    begin transaction;
    set @i += 1;
end
commit
go

次に、「forループ」を実行します。

declare @x int;
select @x = number 
from numbers with(nolock);
select @x as [@x];

結果は次のとおりです。

@x
-----------
88698

(すべての後にあなたが今WTFモーメントを抱えている場合number であるクラスタ化された主キー!)、トリックが呼び出される割り当て順序は、スキャンして、私は挿入されませんでした@j*1000+@i事故で...あなたも持っていた推測を思い切って、その結果があるためであると言うことができます並列性とそれが時々正解かもしれません。

このブリッジの下には多くのトロールがあり、On SQL Serverのブール演算子の短絡T-SQL関数は特定の実行順序を暗示していないことで言及しました


55

さまざまな状況で非常に役立つ数値表を見つけました。

で、私は補助番号のテーブルを使用することを検討すべきであるのはなぜ?、2004年に書かれた、いくつかの例を示します。

  • 文字列の解析
  • アイデンティティのギャップを見つける
  • 日付範囲の生成(例:カレンダーテーブルの作成。これも非常に貴重な場合があります)
  • タイムスライスの生成
  • IP範囲の生成

キックする悪い習慣:大きなテーブルを移入するためにループを使用して、私は数字の表は、行(whileループを使用しての膝ジャークアプローチではなく)の多くを挿入するの短編作品を作るために使用することができる方法を示しています。

で、整数のリストを処理:私のアプローチ分割リストの詳細:カスタム区切り文字、重複を防止し、秩序を維持し、私は文字列を分割するための番号テーブルを使用する方法を示しています(例えば、カンマ区切り値のセット)とパフォーマンスを提供このメソッドと他のメソッドの比較。分割およびその他の文字列処理の詳細:

また、SQL Server Numbers Table、Explained-Part 1では、概念についての背景を説明し、特定のアプリケーションを詳細に説明する将来の投稿を用意しています。

他にも多くの用途がありますが、それらについて書くのに十分なほど目立っているのはほんのわずかです。

そして、@ gbnのように、スタックオーバーフロー、このサイトでは数値テーブルも使用するいくつかの回答があります

最後に、ループなしのセットの生成に関する一連のブログ投稿があります。これは、他のほとんどの方法と比較して数値テーブルを使用することのパフォーマンス上の利点を示しています(Remusの風変わりな外れ値は別として)。


26

以下は、最近Adam Machanicから使用した素晴らしい例です

CREATE FUNCTION dbo.GetSubstringCount
(
    @InputString TEXT, 
    @SubString VARCHAR(200),
    @NoisePattern VARCHAR(20)
)
RETURNS INT
WITH SCHEMABINDING
AS
BEGIN
    RETURN 
    (
        SELECT COUNT(*)
        FROM dbo.Numbers N
        WHERE
            SUBSTRING(@InputString, N.Number, LEN(@SubString)) = @SubString
            AND PATINDEX(@NoisePattern, SUBSTRING(@InputString, N.Number + LEN(@SubString), 1)) = 0
            AND 0 = 
                CASE 
                    WHEN @NoisePattern = '' THEN 0
                    ELSE PATINDEX(@NoisePattern, SUBSTRING(@InputString, N.Number - 1, 1))
                END
    )
END

CTEは、サブストリングの特定のインスタンスを見つけるためにa と同様の何かを使用しました(つまり、「このストリングの3番目のパイプを見つける」)。

declare @TargetStr varchar(8000), 
@SearchedStr varchar(8000), 
@Occurrence int
set @TargetStr='a'
set @SearchedStr='abbabba'
set @Occurrence=3;

WITH Occurrences AS (
SELECT Number,
       ROW_NUMBER() OVER(ORDER BY Number) AS Occurrence
FROM master.dbo.spt_values
WHERE Number BETWEEN 1 AND LEN(@SearchedStr) AND type='P'
  AND SUBSTRING(@SearchedStr,Number,LEN(@TargetStr))=@TargetStr)
SELECT Number
FROM Occurrences
WHERE Occurrence=@Occurrence

数値テーブルがない場合、代替手段は何らかのループを使用することです。基本的に、数値テーブルを使用すると、カーソルやループなしでセットベースの反復を実行できます。


5
そして、インラインTVFSで文字列操作を行うための潜んでいる危険性について必須警告:T-SQL関数には、実行の特定の順序を意味するものでないか
レムスRusanu

12

Enumerable.Rangeに相当するSQLが必要な場合は、常に数値テーブルを使用します。たとえば、このサイトの回答で使用しました:順列の数を計算する

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.