1

次の SQL クエリがあります。

SELECT DISTINCT business_key
FROM Memory
WHERE concept <> 'case' OR attrib <> 'status' OR value <> 'closed'

私が達成しようとしているのは、レコードのコンセプト=ケースと属性=ステータスと値=クローズドを持たないすべての一意のビジネス キーを取得することです。すべての一意の business_keys を持つ 500,000 レコードを使用して MySQL でこのクエリを実行すると、約 11 秒と非常に遅くなります。

business_key 列、concept、attrib、および value 列にインデックスを配置しました。また、3 つの列すべて (concept、attrib、value) への複合インデックスを使用してみましたが、結果は同じです。

EXPLAIN EXTENDEDコマンドのスクリーンショットを次に示します。

ここに画像の説明を入力

興味深いことに、distinct 指定子なしでクエリを実行すると、実行が非常に高速になります。

私もこれを試しました:

SELECT DISTINCT m.business_key
FROM Memory m 
WHERE m.business_key NOT IN 
(SELECT c.business_Key 
 FROM Memory c 
 WHERE c.concept = 'case' AND c.attrib = 'status' AND c.value = 'closed')

さらに悪い結果: 約 25 秒

4

3 に答える 3

2

複合(concept, attrib, value, business_key)インデックスを追加して、クエリ(MySQLがこのインデックスを使用することを決定した場合)がテーブル全体を読み取ることなくインデックス内のすべての情報を見つけることができるようにすることができます。

クエリは次と同等です。

SELECT DISTINCT business_key
FROM Memory
WHERE NOT (concept = 'case' AND attrib = 'status' AND value = 'closed')

そしてこれに(おそらく同じ実行計画が得られるでしょう):

SELECT business_key
FROM Memory
WHERE NOT (concept = 'case' AND attrib = 'status' AND value = 'closed')
GROUP BY business_key

インデックスに配置される4つの列はすべてVARCHAR(255)であるため、インデックスの長さはかなり長くなります。MyISAMは1000バイトを超えてはならず、InnoDBは3072を超えてはなりません。

1つの解決策は、最後の部分の長さをカットして、インデックスの長さを1000未満にすることです255+255+255+230 = 995

(concept, attrib, value, business_key(220))

それは機能しますが、パフォーマンスの観点から、インデックスの長さがこれほど長くなるのは実際には良くありません。

もう1つのオプションは、そこに格納する予定のデータに準拠している場合は、これら4つの列のすべてまたは一部の長さを短くすることです。列に255最大値があると予想される場合は、長さを宣言する必要はありません。100

検討できるもう1つのオプションは、これらの4つの列を4つの個別の参照テーブルに配置することです。(または、データが繰り返されている列だけです。business_keyデータが重複しているように見えますが、それほど多くはありません。したがって、その列の参照テーブルを作成するのはあまり良いことではありません。)

例:concept次のような値を新しいテーブルに配置します。

CREATE TABLE Concept_Ref
( concept_id INT AUTO_INCREMENT
, concept VARCHAR(255)
, PRIMARY KEY concept_id
, UNIQUE INDEX concept_idx (concept) 
) ;

INSERT INTO Concept_Ref
  ( concept )
SELECT DISTINCT
    concept
FROM
    Memory ;

Memory次に、次のようにテーブルを変更します。

ALTER TABLE Memory
ADD COLUMN concept_id INT ;

これを行う(1回):

UPDATE 
    Memory m
  JOIN
    Concept_Ref c
      ON c.concept = m.concept
SET m.concept_id = c.concept_id

Memory.concept次に、列を削除します。

ALTER TABLE Memory
DROP COLUMN concept ;

FOREIGN KEYテーブルをMyISAMからInnoDBに変更する場合は、参照を追加することもできます。

4つの列すべてに対して同じことを行うと、テーブル内の新しい複合インデックスの長さMemoryがはるかに短くなるだけでなく、テーブルのサイズもはるかに小さくなります。さらに、これらの列のいずれかを使用する他のインデックスの長さは短くなります。

もちろん、クエリを作成するには4つのJOINが必要です。また、このテーブルのINSERTUPDATEまたはDELETEステートメントは、変更して慎重に設計する必要があります。

しかし、全体として、パフォーマンスは向上すると思います。'case'あなたが今持っているデザインでは、のような値が何度も繰り返されている'status'ようです。'closed'

于 2011-11-23T13:00:12.323 に答える
1

DISTINCTを使用せずにクエリがすばやく実行される場合は、次のことを試してみましたか。

SELECT DISTINCT business_key from
(SELECT business_key
 FROM Memory
 WHERE concept <> 'case' OR attrib <> 'status' OR value <> 'closed') v

于 2011-11-23T13:03:46.757 に答える
1

これにより、インデックスを使用できるようになります。すべての行を取得するには、まだ時間がかかります。

SELECT DISTINCT business_key FROM Memory 
WHERE NOT(concept = 'case' AND attrib AND 'status' AND value = 'closed')
于 2011-11-23T12:56:29.667 に答える