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

最も完了したフィールドによるMySQLクエリの順序

    私の知る限り、MySQLには行のNULL以外のフィールドの数をカウントする機能はありません。

    したがって、私が考えることができる唯一の方法は、明示的な条件を使用することです:

    SELECT * FROM mytable
        ORDER BY (IF( column1 IS NULL, 0, 1)
                 +IF( column2 IS NULL, 0, 1)
                 ...
                 +IF( column45 IS NULL, 0, 1)) DESC;
    

    ...それは罪のように醜いですが、トリックをするべきです。

    TRIGGERを考案して、追加の列「fields_filled」をインクリメントすることもできます。トリガーはUPDATEで費用がかかります 、45のIFはSELECTであなたを傷つけます;より便利なものをモデル化する必要があります。

    SELECTを高速化するために、すべてのフィールドにインデックスを付けることに注意してください 更新時にコストがかかります(そして、インデックス付きフィールドがVARCHARであることは言うまでもなく、45の異なるインデックスはおそらくselectでのテーブルスキャンと同じくらいのコストがかかります )。いくつかのテストを実行しますが、45-IFソリューションが全体的に最良である可能性が高いと思います。

    更新もし テーブル構造を作り直して、ある程度正規化することができます。フィールドをmy_valuesに配置することもできます。 テーブル。次に、「ヘッダーテーブル」(おそらく一意のIDのみを持つ)と「データテーブル」があります。空のフィールドはまったく存在しないため、RIGHT JOINを使用して、入力されたフィールドの数で並べ替えることができます。 、COUNT()で入力されたフィールドをカウントします 。これにより、UPDATEも大幅に高速化されます オペレーション、およびインデックスを効率的に使用できるようになります。

    例(テーブルの設定から2つの正規化されたテーブルの設定まで)

    Customerのセットがあるとしましょう 記録。 ID、ユーザー名、パスワード、電子メールなどの「必須」データの短いサブセットがあります。そうすれば、ニックネーム、アバター、生年月日などの「オプション」データのサブセットがはるかに大きくなります。最初のステップとして、これらすべてのデータがvarcharであると想定します。 (これは、各列が独自のデータ型を持つ可能性がある単一テーブルソリューションと比較すると、一見制限のように見えます)。

    つまり、

    のようなテーブルがあります。
    ID   username    ....
    1    jdoe        etc.
    2    jqaverage   etc.
    3    jkilroy     etc.
    

    次に、optional-dataテーブルがあります。ここで、John Doeはすべてのフィールドに入力し、Joe Q.平均は2つだけで、Kilroyは何も入力していません(たとえ彼がだったとしても ここに)。

    userid  var   val
    1       name  John
    1       born  Stratford-upon-Avon
    1       when  11-07-1974
    2       name  Joe Quentin
    2       when  09-04-1962
    

    MySQLで「単一テーブル」の出力を再現するには、非常に複雑なVIEWを作成する必要があります。 たくさんのLEFT JOIN s。それでも、(userid, var)に基づくインデックスがある場合、このビューは非常に高速になります。 (varのデータ型にvarcharの代わりに数値定数またはSETを使用するとさらに良いです :

    CREATE OR REPLACE VIEW usertable AS SELECT users.*,
        names.val AS name // (1)
    FROM users
        LEFT JOIN userdata AS names ON ( users.id = names.id AND names.var = 'name') // (2)
    ;
    

    論理モデルの各フィールド(「name」など)は、オプションのデータテーブルのタプル(id、'name'、value)に含まれます。

    そして、<FIELDNAME>s.val AS <FIELDNAME>という形式の行が生成されます。 上記のクエリのセクション(1)で、LEFT JOIN userdata AS <FIELDNAME>s ON ( users.id = <FIELDNAME>s.id AND <FIELDNAME>s.var = '<FIELDNAME>') セクション(2)で。したがって、上記のクエリの最初のテキスト行を動的なセクション1、テキスト「FROMユーザー」、および動的に作成されたセクション2と連結することで、クエリを動的に作成できます。

    これを行うと、ビューのSELECTは以前とまったく同じになりますが、現在はJOINを介して2つの正規化されたテーブルからデータをフェッチします。

    EXPLAIN SELECT * FROM usertable;
    

    この設定に列を追加しても、操作がそれほど遅くなることはありません。つまり、このソリューションは適度に拡張性があります。

    INSERTは変更する必要があり(必須データのみを挿入し、最初のテーブルにのみ挿入します)、UPDATEも同様に変更する必要があります。必須データテーブルまたはオプションのデータテーブルの単一行を更新します。ただし、ターゲット行がない場合は、挿入する必要があります。

    したがって、交換する必要があります

    UPDATE usertable SET name = 'John Doe', born = 'New York' WHERE id = 1;
    

    この場合は「upsert」を使用します

    INSERT INTO userdata VALUES
            ( 1, 'name', 'John Doe' ),
            ( 1, 'born', 'New York' )
        ON DUPLICATE KEY UPDATE val = VALUES(val);
    

    (userdata(id、var)にUNIQUE INDEX on userdata(id, var) ON DUPLICATE KEYの場合 動作する)。

    行サイズとディスクの問題によっては、この変更によってパフォーマンスが大幅に向上する場合があります。

    この変更が実行されない場合、既存のクエリはエラーを生成しないことに注意してください-サイレントに失敗します

    たとえば、ここでは2人のユーザーの名前を変更します。 1つはレコードに名前があり、もう1つはNULLです。 1つ目は変更され、2つ目は変更されません。

    mysql> SELECT * FROM usertable;
    +------+-----------+-------------+------+------+
    | id   | username  | name        | born | age  |
    +------+-----------+-------------+------+------+
    |    1 | jdoe      | John Doe    | NULL | NULL |
    |    2 | jqaverage | NULL        | NULL | NULL |
    |    3 | jtkilroy  | NULL        | NULL | NULL |
    +------+-----------+-------------+------+------+
    3 rows in set (0.00 sec)
    mysql> UPDATE usertable SET name = 'John Doe II' WHERE username = 'jdoe';
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    mysql> UPDATE usertable SET name = 'James T. Kilroy' WHERE username = 'jtkilroy';
    Query OK, 0 rows affected (0.00 sec)
    Rows matched: 0  Changed: 0  Warnings: 0
    mysql> select * from usertable;
    +------+-----------+-------------+------+------+
    | id   | username  | name        | born | age  |
    +------+-----------+-------------+------+------+
    |    1 | jdoe      | John Doe II | NULL | NULL |
    |    2 | jqaverage | NULL        | NULL | NULL |
    |    3 | jtkilroy  | NULL        | NULL | NULL |
    +------+-----------+-------------+------+------+
    3 rows in set (0.00 sec)
    

    各行のランクを知るために、ランクを持っているユーザーについては、IDごとのuserdata行の数を取得するだけです。

    SELECT id, COUNT(*) AS rank FROM userdata GROUP BY id
    

    ここで、「満たされたステータス」の順序で行を抽出するために、次のことを行います。

    SELECT usertable.* FROM usertable
        LEFT JOIN ( SELECT id, COUNT(*) AS rank FROM userdata GROUP BY id ) AS ranking
    ON (usertable.id = ranking.id)
    ORDER BY rank DESC, id;
    

    LEFT JOIN ランクのない個人も確実に取得され、idによる追加の順序付けが行われます。 同じランクの人が常に同じ順序で出てくるようにします。




    1. PHPの日時スタンプが正しいことを確認しますか?

    2. エラー:互換性のない文字エンコード:UTF-8およびASCII-8BIT

    3. PHPOpensslはAESMysql暗号化を復号化します

    4. 次のクエリの結果を取得します:-