sql >> データベース >  >> RDS >> Sqlserver

SQL、数値の補助テーブル

    ええと...すみません、古い投稿への返信が遅くなりました。そして、ええ、このスレッドで最も人気のある回答(当時、14の異なるメソッドへのリンクを含む再帰CTEの回答)は、うーん...パフォーマンスに問題があるため、応答する必要がありました。

    まず、14の異なるソリューションを含む記事は、Numbers / Tallyテーブルをその場で作成するさまざまな方法を確認するのに適していますが、記事と引用されたスレッドで指摘されているように、非常に 重要な引用...

    「効率とパフォーマンスに関する提案は主観的なものであることがよくあります。クエリの使用方法に関係なく、物理的な実装によってクエリの効率が決まります。したがって、偏ったガイドラインに頼るのではなく、クエリをテストしてどちらのパフォーマンスが優れているかを判断することが不可欠です。」

    皮肉なことに、記事自体には多くの主観的なステートメントと、「再帰CTEはかなり効率的に」番号リストを生成できるなどの「偏ったガイドライン」が含まれています。 " および「これは効率的な方法です ItzikBen-Genによるニュースグループの投稿からWHILEループを使用する方法」 (これは、比較のために彼が投稿したと確信しています)。さあ、みんな... 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
    

    その間、SQL Profilerから取得した100、1000、10000、100000、および1000000の値の数値を次に示します...

    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メソッドは、Whileループの期間とCPUに次ぐ2番目に悪い方法であり、論理読み取りの形式でのメモリ負荷は、Whileループの8倍です。 。これはステロイドのRBARであり、Whileループを回避する必要があるのと同じように、単一行の計算では絶対に回避する必要があります。 再帰が非常に価値のある場所がありますが、これはその1つではありません

    サイドバーとして、デニー氏は絶対に注目を集めています...正しいサイズのパーマネントナンバーまたはタリーテーブルは、ほとんどのことを行うための方法です。正しいサイズとはどういう意味ですか?ほとんどの人は、Tallyテーブルを使用して日付を生成したり、VARCHAR(8000)で分割を行ったりします。 「N」に正しいクラスター化インデックスを使用して11,000行のTallyテーブルを作成すると、30年以上の日付を作成するのに十分な行があります(私は住宅ローンをかなり扱っているので、30年が重要な数値です)そして確かにVARCHAR(8000)分割を処理するのに十分です。 「適切なサイズ設定」が非常に重要なのはなぜですか。 Tallyテーブルが頻繁に使用される場合は、キャッシュに簡単に収まるため、メモリに大きな負担をかけることなく、非常に高速になります。

    最後になりましたが、永続的なTallyテーブルを作成する場合、1)1回だけ作成され、2)11,000行のようなものであるため、どの方法を使用して作成してもかまいません。表では、すべてのメソッドが「十分に」実行されます。 では、なぜどの方法を使用するかについての私の側のすべての憤慨???

    答えは、よくわからず、仕事を終わらせる必要がある貧しい人/ギャルが、再帰CTEメソッドのようなものを見て、ビルドよりもはるかに大きく、はるかに頻繁に使用されるものに使用することを決定する可能性があるということです永続的なTallyテーブルであり、それらの人々、コードが実行されているサーバー、およびそれらのサーバー上のデータを所有している会社を保護しようとしています 。ええ...それは大したことです。それは他のすべての人にも当てはまるはずです。 「十分に良い」のではなく、物事を行う正しい方法を教えてください。投稿や本から何かを投稿したり使用したりする前に、いくつかのテストを行ってください...実際、再帰CTEがこのようなものを選択する方法であると考える場合は、実際、節約できる命はあなた自身のものかもしれません。;-)

    聞いてくれてありがとう...



    1. Node.js MSSQL tedius ConnectionError:localhost:1433への接続に失敗しました-ECONNREFUSEDに接続します

    2. MySQLテーブルでのvarcharの長さの重要性

    3. SQL Server:SQLクエリを使用してテーブルの主キーを取得します

    4. ADOを使用してC#で効率的なSQL Serverデッドロック処理を取得するにはどうすればよいですか?