MySQLで条件付きインデックスを作成する方法は?


24

MySQLのテーブルの特定の範囲またはサブセットをフィルターするインデックスを作成する方法は?知る限りでは、直接作成することはできませんが、この機能をシミュレートすることは可能だと思います。

例:次のNAME行だけの列のインデックスを作成したいSTATUS = 'ACTIVE'

この機能は、SQL Serverではフィルター選択されたインデックス、Postgres では部分インデックスと呼ばれます。

回答:


9

MySQLは現在、条件付きインデックスをサポートしていません。

あなたが求めていることを達成するために(あなたはそれをするべきではありません;))補助テーブルの作成を始めることができます:

CREATE TABLE  `my_schema`.`auxiliary_table` (
   `id` int unsigned NOT NULL,
   `name` varchar(250), /* specify the same way as in your main table */
   PRIMARY KEY (`id`),
   KEY `name` (`name`)
);

次に、メインテーブルに3つのトリガーを追加します。

delimiter //

CREATE TRIGGER example_insert AFTER INSERT ON main_table
FOR EACH ROW
BEGIN
   IF NEW.status = 'ACTIVE' THEN
      REPLACE auxiliary_table SET
         auxiliary_table.id = NEW.id,
         auxiliary_table.name = NEW.name;
   END IF;
END;//

CREATE TRIGGER example_update AFTER UPDATE ON main_table
FOR EACH ROW
BEGIN
   IF NEW.status = 'ACTIVE' THEN
      REPLACE auxiliary_table SET
         auxiliary_table.id = NEW.id,
         auxiliary_table.name = NEW.name;
   ELSE
      DELETE FROM auxiliary_table WHERE auxiliary_table.id = OLD.id;
   END IF;
END;//

CREATE TRIGGER example_delete AFTER DELETE ON main_table
FOR EACH ROW
BEGIN
   DELETE FROM auxiliary_table WHERE auxiliary_table.id = OLD.id;
END;//

delimiter ;

トリガー内でdelimiter //使用したいので必要です;

このように、補助テーブルには、トリガーによって更新される文字列「ACTIVE」を含むメインテーブルの行に対応するIDが正確に含まれます。

でそれを使用するにはselect、通常のものを使用できますjoin

SELECT main_table.* FROM auxiliary_table LEFT JOIN main_table
   ON auxiliary_table.id = main_table.id
   ORDER BY auxiliary_table.name;

メインテーブルに既にデータが含まれている場合、またはデータを異常な方法で変更する外部操作を行う場合(例:MySQLの外部)、これで補助テーブルを修正できます。

INSERT INTO auxiliary_table SET
   id = main_table.id,
   name = main_table.name,
   WHERE main_table.status="ACTIVE";

パフォーマンスについては、おそらく挿入、更新、削除が遅くなります。これは、必要な条件が肯定的であるいくつかのケースを本当に処理する場合にのみ意味をなすことができます。その方法でさえ、おそらく、節約されたスペースが本当にこのアプローチを正当化するかどうかを確認できるのはテストだけです(そして実際にスペースを本当に節約している場合)。


7

私が質問を正しく理解していれば、あなたがやろうとしていることを達成するのは、NAMEとSTATUSの両方の列にインデックスを作成することだと思います。これにより、NAME = 'SMITH'およびSTATUS = 'ACTIVE'の場所を効率的に照会できます。


1
ただし、ステータスがACTIVEの行が比較的少ない場合、これはスペース効率がよくありません。
マニエロ

いいえ、そうではありませんが、それは質問の要件ではなく、テーブルが値の1つに大きく重み付けされているとは述べられていません。そのために、私はあなたが探しているSTATUSの実体化されたビューを作成しますが、MySQLはそれらをサポートしていません。
BlackICE

ディスクスペースは安価です
...-BlackICE

2
はい、それは直接的な要件ではないので、私はコメントをOKで始めました。プロの代替品を探しています。そして、あなたの仕事をするための最も効率的な方法を常に探しているプロの選択肢。あなたの答えはおそらく最も明白なものでしょう。問題ありません。しかし、私は「ディスクスペースが安い」ことにまったく同意しません。それは高価だからではなく、もちろん安いからです。しかし、メモリはそれほど安くなく、メモリには制限があり、インデックスは主にメモリ上で有効である必要があります。ディスクアクセスはそれほど安くはありません。あなたの答えは確かに目標を達成するための正しい方法の1つですが、私はそれが最良であるとは思いません。
マニエロ

私も記憶に同意しません、最近もかなり安いです(確かにディスク容量ほど安くはありませんが、その中の1ギガあたり10ドルで、少し
散財

6

条件付きインデックスを作成することはできませんが、この例では、(namestatus)に複数列のインデックスを追加できます。

これらの列のすべてのデータのインデックスが作成されますが、ステータスが「アクティブ」の探している名前を見つけるのに役立ちます。


4

これを行うには、2つのテーブル間でデータを分割し、ビューを使用してすべてのデータが必要なときに2つのテーブルを結合し、その列のテーブルの1つだけにインデックスを付けます。ただし、これにより、クエリプランナーが私が信用している以上に賢い場合を除き、テーブル全体を実行します。基本的に、手動でテーブルをパーティション分割します(そしてインデックスをパーティションの1つだけに適用します)。

残念ながら、単一のパーティションにインデックスを適用することはできないため、組み込みのテーブルパーティション機能は、あなたの探求に役立ちません。

インデックスを使用して追加の列を維持し、インデックスの基にする条件がtrueの場合にのみその列に値を持つことができますが、これは労働集約的であり、クエリの効率とスペースの節約。


より良いインデックスを作成するためだけに2つのテーブルを用意することはありません。結合は依然として高価になるからです。
jcolebrand

@jcolebrand:一般的なクエリ(ユニオンを実行するビューよりも)の方が高価になるため、インデックスを使用するにはパーティションテーブルから特に選択する必要があります。ビルトインパーティショニングはこれを効率的に行いますが、Bigownが(スペースを節約するために)パーティション固有のインデックスをサポートする場合にのみ必要な方法です。私は彼それができると言った、彼がしたいとは思わなかった!
デヴィッドスピレット

0

MySQLには仮想列があり、インデックスに使用できます。


3
この機能を使用して、フィルター処理されたインデックスをシミュレートするにはどうすればよいですか?
ypercubeᵀᴹ

1
@ yper-trollᵀᴹ、druud62は、Oracleの考え方かもしれません:dbfiddle.uk/... -かかわらず、MySQLは御馳走のNULLに同じように見ていません:dbfiddle.uk/...
ジャック・ダグラス

おそらく@JackDouglas。(これは、単にスペースを節約するインデックス最適化ではありませんか?つまりselect count(*) from foo where id is null ;、インデックスを使用できますか?)
ypercubec

@yper-trollᵀᴹOracleは、すべてのインデックス付けされた列がNULLである行をインデックス付けしません(use-the-index-luke.com/sql/where-clause/null/index)—decode(status,'ACTIVE',name,null)たとえば、仮想列をオンにすることができます。
ジャックダグラス

Thnx、私はそれが最近のバージョンで変更されたと思った(そしてヌルがインデックス付けされた)
ypercubeᵀᴹ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.