2008-08-14 14 views
54

特定の種類のSQL問合せでは、補助表が非常に便利です。これは、特定のタスクに必要な数の行を持つ表として、または各照会で必要な行数を戻すユーザー定義関数として作成できます。SQL、数字の補助テーブル

このような関数を作成する最適な方法は何ですか?

+1

から取られたアイデアあらかじめ数を記入したテーブルを使用するよりもrs? – jammus

+7

たとえば、そのような表を埋める。 – vzczc

+4

すべてのDBAおよび/またはサードパーティのアプリケーションで永続的なテーブルの追加が許可されるわけではありません。 – JeffO

答えて

101

をふむ...残念私はこんなに遅く古いポストへの対応です。そして、ええと、このスレッドで最も人気のある回答(その時点では、14種類のメソッドへのリンクを持つ再帰CTEの回答)が、umm ...最高のパフォーマンスに挑戦したので、私は対応しなければなりませんでした。

まず、Numbers/Tallyテーブルを作成するさまざまな方法を見ても問題ありませんが、記事や引用されたスレッドで指摘されているように、は非常にです。 ...

「効率と パフォーマンスに関する提案はしばしば主観的である。 かかわらず、クエリは が使用されているかの、物理的な実装 は、クエリの効率を決定する。 ためではなく、 に頼りますバイアスdガイドラインでは、クエリをテストしてより良いパフォーマンスを示す を決定することが重要です( )。

皮肉なことに、記事自体は、多くの主観的な文や、などの「偏ったガイドライン」が含まれ「再帰CTEはかなり効率的ををリスト番号を生成することができます」「これは使用しての効率的な方法ですItzik Ben-Genの投稿ニュースグループのWHILEループ "(私は彼が比較目的のために投稿したと確信しています)。 C'mon people ...ちょうどItzikの良い名前に言及すると、実際にはその恐ろしい方法を使用することにいくつかの貧しい人を導く可能性があります。著者は説教したことを練習し、不自然に誤解を招くような文章を書く前に少しのパフォーマンステストを行うべきです。

コードが何をしているのか、誰かが「好き」であることについて主観的な主張をする前に、実際にいくつかのテストを行うことを考えて、ここであなた自身のテストを行うことができるコードです。テストを実行しているSPIDのセットアッププロファイラを自分でチェックしてください...あなたの「お気に入り」番号の番号1000000の「Search'n'Replace」を実行してください...

--===== Test for 1000000 rows ================================== 
GO 
--===== Traditional RECURSIVE CTE method 
    WITH Tally (N) AS 
     ( 
     SELECT 1 UNION ALL 
     SELECT 1 + N FROM Tally WHERE N < 1000000 
     ) 
SELECT N 
    INTO #Tally1 
    FROM Tally 
OPTION (MAXRECURSION 0); 
GO 
--===== Traditional WHILE LOOP method 
CREATE TABLE #Tally2 (N INT); 
    SET NOCOUNT ON; 
DECLARE @Index INT; 
    SET @Index = 1; 
    WHILE @Index <= 1000000 
    BEGIN 
     INSERT #Tally2 (N) 
     VALUES (@Index); 
      SET @Index = @Index + 1; 
    END; 
GO 
--===== Traditional CROSS JOIN table method 
SELECT TOP (1000000) 
     ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS N 
    INTO #Tally3 
    FROM Master.sys.All_Columns ac1 
    CROSS JOIN Master.sys.ALL_Columns ac2; 
GO 
--===== Itzik's CROSS JOINED CTE method 
    WITH E00(N) AS (SELECT 1 UNION ALL SELECT 1), 
     E02(N) AS (SELECT 1 FROM E00 a, E00 b), 
     E04(N) AS (SELECT 1 FROM E02 a, E02 b), 
     E08(N) AS (SELECT 1 FROM E04 a, E04 b), 
     E16(N) AS (SELECT 1 FROM E08 a, E08 b), 
     E32(N) AS (SELECT 1 FROM E16 a, E16 b), 
    cteTally(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY N) FROM E32) 
SELECT N 
    INTO #Tally4 
    FROM cteTally 
    WHERE N <= 1000000; 
GO 
--===== Housekeeping 
    DROP TABLE #Tally1, #Tally2, #Tally3, #Tally4; 
GO 

我々はそれに取り組んでいる一方で、ここで私は100、1000年、10000、100000、および1000000の値に対してSQLプロファイラから取得数字だ...

SPID TextData         Dur(ms) CPU Reads Writes 
---- ---------------------------------------- ------- ----- ------- ------ 
    51 --===== Test for 100 rows ==============  8  0  0  0 
    51 --===== Traditional RECURSIVE CTE method  16  0  868  0 
    51 --===== Traditional WHILE LOOP method CR  73 16  175  2 
    51 --===== Traditional CROSS JOIN table met  11  0  80  0 
    51 --===== Itzik's CROSS JOINED CTE method  6  0  63  0 
    51 --===== Housekeeping DROP TABLE #Tally  35 31  401  0 

    51 --===== Test for 1000 rows =============  0  0  0  0 
    51 --===== Traditional RECURSIVE CTE method  47 47 8074  0 
    51 --===== Traditional WHILE LOOP method CR  80 78 1085  0 
    51 --===== Traditional CROSS JOIN table met  5  0  98  0 
    51 --===== Itzik's CROSS JOINED CTE method  2  0  83  0 
    51 --===== Housekeeping DROP TABLE #Tally  6 15  426  0 

    51 --===== Test for 10000 rows ============  0  0  0  0 
    51 --===== Traditional RECURSIVE CTE method  434 344 80230  10 
    51 --===== Traditional WHILE LOOP method CR  671 563 10240  9 
    51 --===== Traditional CROSS JOIN table met  25 31  302  15 
    51 --===== Itzik's CROSS JOINED CTE method  24  0  192  15 
    51 --===== Housekeeping DROP TABLE #Tally  7 15  531  0 

    51 --===== Test for 100000 rows ===========  0  0  0  0 
    51 --===== Traditional RECURSIVE CTE method 4143 3813 800260 154 
    51 --===== Traditional WHILE LOOP method CR 5820 5547 101380 161 
    51 --===== Traditional CROSS JOIN table met  160 140  479 211 
    51 --===== Itzik's CROSS JOINED CTE method  153 141  276 204 
    51 --===== Housekeeping DROP TABLE #Tally  10 15  761  0 

    51 --===== Test for 1000000 rows ==========  0  0  0  0 
    51 --===== Traditional RECURSIVE CTE method 41349 37437 8001048 1601 
    51 --===== Traditional WHILE LOOP method CR 59138 56141 1012785 1682 
    51 --===== Traditional CROSS JOIN table met 1224 1219 2429 2101 
    51 --===== Itzik's CROSS JOINED CTE method  1448 1328 1217 2095 
    51 --===== Housekeeping DROP TABLE #Tally  8  0  415  0 

あなたが見ることができるように、再帰CTEメソッドはDurationとCPUのWhile Loopの2番目にワーストであり、Whileループよりも論理読み込みの形で8倍のメモリ圧があります。これはステロイドのRBARであり、Whileループを回避する必要があるように、単一行の計算にはすべてのコストをかけて回避する必要があります。 再帰がかなり貴重な場所がありますが、これはどちらでもないです。

サイドバーとして、Mr. Dennyは絶対的な位置にあります。正しいサイズの永久NumbersまたはTallyテーブルは、ほとんどのものに行く方法です。正確なサイズはどういう意味ですか?ほとんどの人は、Tallyテーブルを使って日付を生成するか、VARCHAR(8000)で分割を行います。 "N"に適切なクラスタード・インデックスを持つ11,000行のTallyテーブルを作成すると、30年以上の日付を作成するのに十分な行が得られます(私はモーゲージを公正なビットで処理するので、 )、確かにVARCHAR(8000)分割を処理するのに十分です。 「正しいサイジング」がなぜ重要なのですか? Tallyテーブルが多用されていると、キャッシュに簡単に収まり、メモリに大きな負担をかけずにすばやく高速化できます。

永続的なTallyテーブルを作成すると、作成する方法はあまり関係ありません。なぜなら、1つは1回だけ作成され、2つは何かがあるからです11,000行のテーブルのように、すべてのメソッドが「十分に」実行されます。 なぜ、どのメソッドを使用するかについての私の部分のすべての曖昧さ?

答えがうまくいかず、自分の仕事をやり遂げる必要がある貧しい人/貧しい人は、再帰的なCTEメソッドのようなものを見て、はるかに大きく恒久的なTallyテーブルを構築するよりも頻繁に使用されており、私はの人々、そのコードが実行されているサーバー、およびそれらのサーバー上のデータを所有する会社を保護しようとしています。ええ...それは大きな問題です。他の人にとってもそうであるはずです。 「十分に良い」のではなく、正しいことを教える。ポストや本から何かを投稿する前にいくつかのテストをしてください。保存する人生は、再帰的なCTEがこれのようなものに行く方法であると思うなら、あなた自身のものかもしれません。リスニングのため;-)

おかげで...

+8

+1最も役立つ答え。 –

+0

私は本当に多くの人々があなたの社会的責任感を持ってほしいです。何らかの理由で必要であれば、Numbersテーブルにすべての種類のものを埋め込む必要があると言いましたが、[SELECT INTO'が 'IDENTITY'がCTEより速いようです](https:// stackoverflow .com/a/1407488/986862)。 –

+0

非常に親切なフィードバックありがとう、アンドレ。 –

10

最も最適な関数は、関数の代わりにテーブルを使用することです。関数を使用すると、余分なCPU負荷が発生し、返されるデータの値が作成されます(特に、返される値が非常に大きい場合)。

+1

私はそれがあなたの状況に依存すると思います。 2つの最も優れたオプションの間では、より高価なものに応じてIOとCPUのコストを交換できます。 – Rbjz

+0

IOはCPUよりもずっと安いでしょう。特に、このテーブルは小さく、おそらくは既にbudferpoolにあるでしょう。 – mrdenny

+0

@mrdenny I/Oは常にCPUよりも高価で低速です。 SSDは近年多少変更されていますが、ほとんどの実動アーキテクチャーでは、それらのSSDはそれらのSSDとCPUとの間にネットワークリンクを持っています。本当にCPUに拘束されている唯一のデータベースは、調整されていないORM専用アプリや重い機械学習を実行していることです。 – rmalayter

4

This articleは、それぞれについて14の異なる解決策を示します。重要な点は次のとおりです。

効率と のパフォーマンスに関する提案はしばしば主観的です。 照会がどのように使用されているかにかかわらず、物理的な実装 が照会の効率を決定します。 したがって、 偏向されたガイドラインに頼るのではなく、 というクエリをテストし、どちらが良いかを判断することが重要です( )。

私が個人的に気に入っ:

WITH Nbrs (n) AS (
    SELECT 1 UNION ALL 
    SELECT 1 + n FROM Nbrs WHERE n < 500) 
SELECT n FROM Nbrs 
OPTION (MAXRECURSION 500) 
+2

受け入れられた回答で間違っていることを証明しましたか?それはハンサムに見えるが、それは「最適」ではない。 – Rbjz

0

編集:以下コンラッドのコメントを参照してください。

Jeff Modenの回答は素晴らしいですが、E32の行を削除しない限り、Itzikメソッドが失敗することがPostgresにあります。

のpostgres(40msの対100ミリ秒)で少し速く

は、私はpostgresのために適合さhereで見つかった別の方法である:私は、SQL ServerからPostgresの世界に移動していたよう

WITH 
    E00 (N) AS ( 
     SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
     SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1), 
    E01 (N) AS (SELECT a.N FROM E00 a CROSS JOIN E00 b), 
    E02 (N) AS (SELECT a.N FROM E01 a CROSS JOIN E01 b), 
    E03 (N) AS (SELECT a.N FROM E02 a CROSS JOIN E02 b 
     LIMIT 11000 -- end record 11,000 good for 30 yrs dates 
    ), -- max is 100,000,000, starts slowing e.g. 1 million 1.5 secs, 2 mil 2.5 secs, 3 mill 4 secs 
    Tally (N) as (SELECT row_number() OVER (ORDER BY a.N) FROM E03 a) 

SELECT N 
FROM Tally 

、行うには良い方法を逃したかもしれませんそのプラットフォームの集計表... INTEGER()? SEQUENCE()?

+2

*は[postgres]で集計表を作成するより良い方法を見逃している可能性があります*ええ、あなたは[generate_series](http://www.postgresql.org/docs/8.1/static/functions-srf.html) –

+0

Conradに感謝します。 ..ルーキーエラー。 – Ruskin

3

このビューは超高速であり、すべての正のint値を含んでいます。

CREATE VIEW dbo.Numbers 
WITH SCHEMABINDING 
AS 
    WITH Int1(z) AS (SELECT 0 UNION ALL SELECT 0) 
    , Int2(z) AS (SELECT 0 FROM Int1 a CROSS JOIN Int1 b) 
    , Int4(z) AS (SELECT 0 FROM Int2 a CROSS JOIN Int2 b) 
    , Int8(z) AS (SELECT 0 FROM Int4 a CROSS JOIN Int4 b) 
    , Int16(z) AS (SELECT 0 FROM Int8 a CROSS JOIN Int8 b) 
    , Int32(z) AS (SELECT TOP 2147483647 0 FROM Int16 a CROSS JOIN Int16 b) 
    SELECT ROW_NUMBER() OVER (ORDER BY z) AS n 
    FROM Int32 
GO 
+1

「0」はしばしば役に立ちます。そしておそらく、最終列を 'int'に変換するでしょう。また、基本的には、* ItzikのCROSS JOINED CTEメソッド*の名前で受け入れられた答え( '0'も' 'int''への変換もなく)にメソッドが含まれていることを知っておくべきです。 –

+0

ビューに 'WITH SCHEMABINDING'を追加する特別な理由はありますか? – ca9163d9

+0

'WITH SCHEMABINDING'を追加すると、クエリを高速化できます。データがアクセスされていないことをオプティマイザが認識するのに役立ちます。 (http://blogs.msdn.com/b/sqlprogrammability/archive/2006/05/12/596424.aspxを参照してください) –

0

それでもずっと後、私は少し異なる「伝統的」CTEを(行のボリュームを取得するために、ベーステーブルには触れていません)貢献したいと思います:

--===== Hans CROSS JOINED CTE method 
WITH Numbers_CTE (Digit) 
AS 
(SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) 
SELECT HundredThousand.Digit * 100000 + TenThousand.Digit * 10000 + Thousand.Digit * 1000 + Hundred.Digit * 100 + Ten.Digit * 10 + One.Digit AS Number 
INTO #Tally5 
FROM Numbers_CTE AS One CROSS JOIN Numbers_CTE AS Ten CROSS JOIN Numbers_CTE AS Hundred CROSS JOIN Numbers_CTE AS Thousand CROSS JOIN Numbers_CTE AS TenThousand CROSS JOIN Numbers_CTE AS HundredThousand 

このCTEは、より多くのREAD文を実行ItzikのCTEは従来のCTEよりも低い。 しかし、一貫して少ないWRITESを実行してから、他のクエリを実行します。 あなたが知っているように、書き込みは一貫してかなり高価で、次に読みます。

コアの数(MAXDOP)に大きく依存しますが、私の8コアでは、他のクエリよりも一貫して速く(ms以下の時間)実行されます。

私が使用しています。Windows Server 2012 R2上の

Microsoft SQL Server 2012 - 11.0.5058.0 (X64) 
May 14 2014 18:34:29 
Copyright (c) Microsoft Corporation 
Enterprise Edition (64-bit) on Windows NT 6.3 <X64> (Build 9600:) 

を、32ギガバイト、XeonプロセッサX3450 @ 2.67Ghz、HTを有効に4つのコアが。

1

表あなたがOPENJSON使用できる番号を生成するSQL Server 2016+を使用した:

-- range from 0 to @max - 1 
DECLARE @max INT = 40000; 

SELECT rn = CAST([key] AS INT) 
FROM OPENJSON(CONCAT('[1', REPLICATE(CAST(',1' AS VARCHAR(MAX)),@max-1),']')); 

LiveDemo


あなたは、むしろこれを実行したい理由を説明してもらえ How can we use OPENJSON to generate series of numbers?

+1

ニース。おそらく、SQL ServerのXQueryで 'position()'が完全にサポートされていれば、これと同様にXMLを使用できたと思います。 –

関連する問題