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

異なる行で異なる条件を満たす値を選択しますか?

    わかりました、私はこれに反対票を投じたので、それをテストすることにしました:

    CREATE TABLE userrole (
      userid INT,
      roleid INT,
      PRIMARY KEY (userid, roleid)
    );
    
    CREATE INDEX ON userrole (roleid);
    

    これを実行します:

    <?php
    ini_set('max_execution_time', 120); // takes over a minute to insert 500k+ records 
    
    $start = microtime(true);
    
    echo "<pre>\n";
    mysql_connect('localhost', 'scratch', 'scratch');
    if (mysql_error()) {
        echo "Connect error: " . mysql_error() . "\n";
    }
    mysql_select_db('scratch');
    if (mysql_error()) {
        echo "Selct DB error: " . mysql_error() . "\n";
    }
    
    $users = 200000;
    $count = 0;
    for ($i=1; $i<=$users; $i++) {
        $roles = rand(1, 4);
        $available = range(1, 5);
        for ($j=0; $j<$roles; $j++) {
            $extract = array_splice($available, rand(0, sizeof($available)-1), 1);
            $id = $extract[0];
            query("INSERT INTO userrole (userid, roleid) VALUES ($i, $id)");
            $count++;
        }
    }
    
    $stop = microtime(true);
    $duration = $stop - $start;
    $insert = $duration / $count;
    
    echo "$count users added.\n";
    echo "Program ran for $duration seconds.\n";
    echo "Insert time $insert seconds.\n";
    echo "</pre>\n";
    
    function query($str) {
        mysql_query($str);
        if (mysql_error()) {
            echo "$str: " . mysql_error() . "\n";
        }
    }
    ?>
    
    \ n "; function query($ str){mysql_query($ str); if(mysql_error()){echo "$ str:"。 mysql_error()。 "\ n"; }}?>

    出力:

    499872 users added.
    Program ran for 56.5513510704 seconds.
    Insert time 0.000113131663847 seconds.
    

    これにより、500,000のランダムなユーザーと役割の組み合わせが追加され、選択した基準に一致するものは約25,000になります。

    最初のクエリ:

    SELECT userid
    FROM userrole
    WHERE roleid IN (1, 2, 3)
    GROUP by userid
    HAVING COUNT(1) = 3
    

    クエリ時間:0.312秒

    SELECT t1.userid
    FROM userrole t1
    JOIN userrole t2 ON t1.userid = t2.userid AND t2.roleid = 2
    JOIN userrole t3 ON t2.userid = t3.userid AND t3.roleid = 3
    AND t1.roleid = 1
    

    クエリ時間:0.016秒

    それは正しい。私が提案した参加バージョンは、集約バージョンよりも20倍高速です。

    申し訳ありませんが、私はこれを現実の世界での生活と仕事のために行っています。現実の世界ではSQLをテストし、その結果が物語っています。

    この理由はかなり明確なはずです。集計クエリは、テーブルのサイズに応じてコストがスケーリングされます。すべての行は、HAVINGを介して処理、集約、およびフィルタリングされます(またはされません)。 句。参加バージョンは、(インデックスを使用して)特定の役割に基づいてユーザーのサブセットを選択し、そのサブセットを2番目の役割と照合し、最後にそのサブセットを3番目の役割と照合します。各選択 (関係代数 用語)はますます小さなサブセットで機能します。これから、次のように結論付けることができます:

    参加バージョンのパフォーマンスは、一致の発生率が低くなるとさらに向上します。

    上記の3つの役割を持つユーザーが500人しかいない場合(上記の500kのサンプルのうち)、参加バージョンは大幅に高速になります。集約バージョンはそうではありません(そして、パフォーマンスの向上は、25kではなく500ユーザーを転送した結果であり、結合バージョンも明らかに得られます)。

    また、実際のデータベース(つまり、Oracle)がこれをどのように処理するかについても興味がありました。そのため、基本的にOracle XE(前の例のMySQLと同じWindows XPデスクトップマシンで実行)で同じ演習を繰り返しましたが、結果はほぼ同じです。

    結合は眉をひそめているように見えますが、私が示したように、集計クエリは桁違いに遅くなる可能性があります。

    更新: いくつかの広範なテスト の後 、画像はより複雑であり、答えはデータ、データベース、およびその他の要因によって異なります。物語の教訓は、テスト、テスト、テストです。



    1. テーブルの名前付けのジレンマ:単数形と複数形

    2. 文字列をコンマで結合するためのOracle集計、およびカスタム集計の記述について

    3. SQL Server(T-SQL)で列のデータ型を変更する方法

    4. libpqソースはどこで入手できますか?