web-dev-qa-db-ja.com

文字列列のascとdescの両方にインデックスを使用する

Ascとdescで並べ替える場合に、クエリによる並べ替えを高速化するトリックを知っています。
整数の場合、否定値を格納します。
したがって、私が保存する場合-7,-11,-8,-5,-1,-2 ascによる注文は-11, -8, -7, 5, -2, -1なので、実際の行はその列でdescでソートされます(たとえば、否定されていないバージョンを表示する場合)。
質問:このトリックを文字列でどのように使用しますか?
トリックが文字列列でも同様に機能するように、文字列はどのように「否定」または反転されますか?

更新
MySQLは次のタイプのクエリにインデックスを使用できません:
ORDER BY col1 ASC col2 DESC
ディセントしたい列が整数の場合、否定値を保存して次のようにクエリを実行できます。
ORDER BY col1 ASC col2 ASC
この場合、インデックスを使用でき、実際には否定のために降順で値を取得します-11, -8, -7, 5, -2, -1は昇順ですが、否定を削除すると降順になります。
私の問題は、この方法を文字列列にどのように適用できるかがはっきりしないことです。どういうわけか文字列の算術値を取得できますか?

4
Cratylus

CREATE INDEXの構文は、インデックスを昇順または降順として定義するオプションをサポートしていますが、最も一般的な2つのストレージエンジン(InnoDBとMyISAM)では、このインデックスオプションは何もしません。 「昇順」または「降順」のインデックスなどはありません。これらは単なるインデックスであり、どちらの方向のソートにも使用できます。

しかし、複数列のインデックスがあり、一方の列を昇順、もう一方の列を降順に並べたい場合については、話は異なります。インデックスを使用する場合、ソート順は両方の列で同じ方向でなければなりません。

(すべての並べ替えでインデックスを使用する必要があるわけではありません。クエリを実行して、一致する行をfilesortで並べ替えることができます。)

文字列の並べ替え順序を逆にするには、独自のカスタム照合を追加します。 https://dev.mysql.com/doc/refman/5.6/en/adding-collat​​ion.html

しかし、その新しい照合を使用して文字列を保存し、インデックスを再構築する必要があります。インデックスはonlyが逆ソートに役立ちます。


@Cratylusからの再コメント:

照合とは、「A」を「B」およびその他のすべての文字の順序の前にあるものとして定義するものです。したがって、「B」が「A」の前にあると言う照合を定義し、その照合を列定義で使用すると、その列でのソート、または列でのインデックスの作成は逆の順序になります。

注:以下は適切な照合順序を作成するのではなく、それを作成する手順を示すだけです。

  1. /usr/share/mysql/charsets/Index.xmlを編集して、次の編集を行います。

    <charset name="latin1">
    
      . . . leave other collations alone . . .
    
      <collation name="latin1_reverse_ci" id="251">
        <order>Dutch</order>
        <order>English</order>
        <order>French</order>
        <order>German Duden</order>
        <order>Italian</order>
        <order>Latin</order>
        <order>Portuguese</order>
        <order>Spanish</order>
      </collation>
    
    </charset>
    
  2. /usr/share/mysql/charsets/latin1.xmlを編集して、次の編集を行います。

    <charset name="latin1">
    
    . . . leave other collations alone . . .
    
    <collation name="latin1_reverse_ci">
    <map>
     AE B1 AC A2 A0 9E 9C 8D BE 8B 89 87 85 83 7F 59
     73 71 6F 6D 63 61 5F 5D 55 4F 4D 4B 49 47 45 43
     97 B1 AC A2 A0 9E 9C 8D BD 8B 89 87 85 83 7F 59
     73 71 6F 6D 63 61 5F 5D 55 4F 4D 4B 49 47 45 43
     FF FE FD FC FB FA F9 F8 F7 F6 F5 F4 F3 F2 F1 F0
     EF EE ED EC EB EA E9 E8 E7 E6 E5 E4 E3 E2 E1 E0
     DF DE DD DC DB DA D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
     CF CE CD CC CB CA C9 C8 C7 C6 C5 C4 C3 C2 C1 C0
     BF BC BB BA B9 AF AA A8 A6 A4 9A 98 95 93 91 8F
     81 7D 7B 79 77 75 6B 69 67 65 5B 57 53 51 41 B8
     B7 B6 B5 B4 B3 AF AA A8 A6 A4 9A 98 95 93 91 8F
     81 7D 7B 79 77 75 6B 69 67 65 5B 57 53 51 41 40
     3F 3E 3D 3C 3B 3A 39 38 37 36 35 34 33 32 31 30
     2F 2E 2D 2C 2B 2A 29 28 27 26 25 24 23 22 21 20
     1F 1E 1D 1C 1B 1A 19 18 17 16 15 14 13 12 11 10
     0F 0E 0D 0C 0B 0A 09 08 07 06 05 04 03 02 01 00
    </map>
    </collation>
    
    </charset>
    . . .
    
  3. Mysqldを再起動します。

  4. 新しい照合があることを確認します。

    mysql> SHOW COLLATION LIKE 'latin1%';
    +---------------------+---------+-----+---------+----------+---------+
    | Collation           | Charset | Id  | Default | Compiled | Sortlen |
    +---------------------+---------+-----+---------+----------+---------+
    . . .
    | latin1_general_ci   | latin1  |  48 |         | Yes      |       1 |
    | latin1_general_cs   | latin1  |  49 |         | Yes      |       1 |
    | latin1_spanish_ci   | latin1  |  94 |         | Yes      |       1 |
    | latin1_reverse_ci   | latin1  | 251 |         |          |       0 |
    . . .
    
  5. テーブルを作成し、テキストを入力します。

    mysql> CREATE TABLE foo (
        id INT AUTO_INCREMENT PRIMARY KEY,
        t VARCHAR(60) COLLATE latin1_reverse_ci, 
        x DATE,
        KEY(t)
    );
    
    mysql> INSERT INTO foo (t) VALUES ('Harry'), ('Ron'), ('Hermione');
    
  6. 並べ替え順序が、本来あるべき順序とは逆であることを示します。

    mysql> SELECT * FROM foo ORDER BY t ASC;
    +----+----------+------+
    | id | t        | x    |
    +----+----------+------+
    |  2 | Ron      | NULL |
    |  3 | Hermione | NULL |
    |  1 | Harry    | NULL |
    +----+----------+------+
    
  7. Filesortを使用していないことを確認します。

    mysql> EXPLAIN SELECT * FROM foo WHERE t LIKE 'H%' ORDER BY t\G
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: foo
       partitions: NULL
             type: range
    possible_keys: t
              key: t
          key_len: 63
              ref: NULL
             rows: 2
         filtered: 100.00
            Extra: Using index condition
    

    行に条件を設定する必要がありました。そうしないと、テーブル内のすべての行を読み取ると考えられ、テーブルスキャンとファイルソートが行われました。


@ypercubeからの再コメント:

REVERSE()のように特定の文字列内の文字の順序を逆にするのではなく、2つの文字列を比較するときに、どの文字が互いに大きいか小さいかを逆にします。したがって、いいえ、CHARパディングスペースは関係ありません。これらのスペースは両方の文字列の最後にあります。

例(今回はインデックスなし、order byのみを使用):

mysql> create table t (c char(20) collate latin1_reverse_ci);
mysql> insert into t values ('A'), ('B'), ('C');
mysql> select * from t order by c;
+------+
| c    |
+------+
| C    |
| B    |
| A    |
+------+

編集:いいえ、@ ypercubeは正しいです。すべてのバイトを逆の順序で並べ替えるだけで単純に照合順序を並べ替えるとスペース文字が間違った場所にあるため、これはCHARでは機能しません。これは、照合順序を設計する適切な方法ではありません。

したがって、この回答は、アルファベット順を逆にするための適切な照合ではなく、照合を作成するメカニズムのデモとして機能する必要があります。

4
Bill Karwin