0

だから私は連絡先を保存し、それらをグループに入れることができるシステムを持っています。これらのグループは、基準(「スミス」という名前の全員)によって、または明示的に人を追加/除外することによって定義できます。

私が抱えている問題は、メーリンググループを一覧表示するときに、各グループに含まれる連絡先の数を数える必要があることです。この番号は、連絡先が連絡先テーブルに追加/削除されると変更される可能性があります。少人数のグループ/連絡先の数では問題ありませんが、50kishの連絡先を使用すると問題が発生します

これに使用するクエリの例は次のとおりです。

SELECT COUNT(c_id) FROM contacts, mgroups
LEFT JOIN mgroups_explicit ON mg_id = me_mg_id
WHERE mgroups.site_id = '10'
AND mg_id = '20'
AND me_c_id = c_id
AND contacts.site_id = '10'
OR (contacts.site_id = '10' AND ( c_tags LIKE '%tag1%')) AND c_id NOT IN
( SELECT mex_c_id FROM mgroups_exclude WHERE c_id = mex_c_id ) GROUP BY c_id 

大きなグループが基準ではなく明示的に作成されたときに問題が発生するため、基準テーブルはこのクエリでは機能しません。これは、連絡先を変更するときに基準ベースのグループがその場で拡大または縮小するときに必要ですが、明示的には一般的に石で設定されます。したがって、この場合、グループに20kの連絡先を明示的に追加すると、そのmg_idが外部キーとしてマークされたテーブルに20kの行が追加されます。

これは基本的に年齢がかかる/タイムアウトする/間違った番号を取得する/一般的にうまく機能しません。より効率的なクエリを理解するか、すべてを保存するためのより良い方法を理解する必要があります。

何か案は?

データベースを構成する5つのメインテーブル

contacts - where the actual contacts reside
Field   Type    Null    Default     Comments
c_id    int(8)  No           
site_id     int(6)  No           
c_email     varchar(500)    No           
c_source    varchar(255)    No           
c_subscribed    tinyint(1)  No      0    
c_special   tinyint(1)  No      0    
c_domain    text    No           
c_title     varchar(12)     No           
c_name  varchar(128)    No           
c_surname   varchar(128)    No           
c_company   varchar(128)    No           
c_jtitle    text    No           
c_ad1   text    No           
c_ad2   text    No           
c_ad3   text    No           
c_county    varchar(64)     No           
c_city  varchar(128)    No           
c_postcode  varchar(32)     No           
c_lat   varchar(100)    No           
c_lng   varchar(100)    No           
c_country   varchar(64)     No           
c_tel   varchar(20)     No           
c_mob   varchar(20)     No           
c_dob   date    No           
c_registered    datetime    No           
c_updated   datetime    No           
c_twitter   varchar(255)    No           
c_facebook  varchar(255)    No           
c_tags  text    No           
c_special_1     text    No           
c_special_2     text    No           
c_special_3     text    No           
c_special_4     text    No           
c_special_5     text    No           
c_special_6     text    No           
c_special_7     text    No           
c_special_8     text    No           

mgroups - basic mailing group info
Field   Type    Null    Default     Comments
mg_id   int(8)  No           
site_id     int(6)  No           
mg_name     varchar(255)    No           
mg_created  datetime    No           

mgroups_criteria - criteria for said mailing groups
Field   Type    Null    Default     Comments
mc_id   int(8)  No           
site_id     int(6)  No           
mc_mg_id    int(8)  No           
mc_criteria     text    No           

mgroups_exclude - anyone to exclude from criteria
Field   Type    Null    Default     Comments
mex_id  int(8)  No           
site_id     int(6)  No           
mex_c_id    int(8)  No           
mex_mg_id   int(8)  No           

mgroups_explicit - anyone to explicitly add without the use of criteria
Field   Type    Null    Default     Comments
me_id   int(8)  No           
site_id     int(6)  No           
me_c_id     int(8)  No           
me_mg_id    int(8)  No

そして、インデックス/クエリの説明。認める必要があります、インデックスは私の強みではありません、何か改善はありますか?

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   PRIMARY     mgroups     ALL     PRIMARY,mg_id   NULL    NULL    NULL    9   Using temporary; Using filesort
1   PRIMARY     mgroups_explicit    ref     me_mg_id    me_mg_id    4   engine_4.mgroups.mg_id  8750     
1   PRIMARY     contacts    ALL     PRIMARY,c_id    NULL    NULL    NULL    86012   Using where; Using join buffer
2   DEPENDENT SUBQUERY  NULL    NULL    NULL    NULL    NULL    NULL    NULL    Impossible WHERE noticed after reading const table...
4

2 に答える 2

1

上記のスキーマにインデックスが表示されていません。インデックスはありますか?

クエリでExplainを実行します

EXPLAIN 
SELECT COUNT(c_id) FROM
   contacts, mgroups LEFT JOIN mgroups_explicit ON mg_id = me_mg_id
WHERE 
   mgroups.site_id = '10' 
   AND mg_id = '20' 
   AND me_c_id = c_id 
   AND contacts.site_id = '10' 
   OR (contacts.site_id = '10' 
   AND ( c_tags LIKE '%tag1%')) 
   AND c_id NOT IN (SELECT mex_c_id FROM mgroups_exclude WHERE c_id = mex_c_id ) GROUP BY c_id

これにより、使用されているインデックスの数などを知ることができます。

DC

于 2011-01-19T23:41:50.177 に答える
0

そのため、他の場所でこれに回答しました(Hambut_Bulgeに感謝します)。他の人に役立つように、ここで解決策を示します。


まず最初に、同じクエリで新旧(ANSI)スタイルの結合を混合します。これは、SQLサークルでは悪い考えと見なされています。古いスタイルとは、これらの行に沿って結合を使用してクエリを作成することを意味します

SELECT a.column_name, b.column2
FROM table1 a, second_table b
WHERE a.id_key = b.fid_key
AND b.some_other_criteria = 'Y';

新しいANSIスタイルでは、上記を次のように書き直します。

SELECT a.column_name, b.column2
FROM table1 a INNER JOIN second_table b ON a.id_key = b.fid_key
WHERE b.some_other_criteria = 'Y';

どのビットが結合条件で、どのビットがwhere句であるかがわかりやすく読みやすくなっています。古いスタイルのサポートは(ある時点で)中止される可能性があるため、ANSIスタイルを使用する習慣を身に付けることも最善です。

また、ドット表記やエイリアスの使用に一貫性を持たせるようにしてください。ここでも、大きなクエリが読みやすくなります。

問題のクエリに戻ると、ANSIスタイルへの変換を開始しましたが、連絡先とmgroupの間に結合条件がないことにすぐに気付きました。これは、オプティマイザがクロス結合(デカルト積とも呼ばれる)を作成することを意味します。これはおそらく、実行したくないことです。クロス結合(知らなかった場合)は、contactsテーブルのすべての行をmgroupsテーブルのすべての行に結合します。したがって、連絡先に50,000行、mgroupに20,000行がある場合、1,000,000,000行を含む結合された結果セットを取得します。

このクエリを大幅に遅くするもう1つのことは、mgroups_excludeのサブクエリです。サブクエリは、外部クエリの行ごとに1回実行されます。例:

SELECT a.column1
FROM table1 a
WHERE a.id_key NOT IN ( SELECT * FROM table2 b WHERE a.id_key = b.fid_key);

table1には2,000,000行、table2には500,000行があるとします。外部クエリ(table1)のすべての行について、データベースは内部クエリに対してフルスキャンを実行する必要があります。したがって、結果を取得するために、データベースは1,000,000,000,000行を読み取り、1,000にしか関心がない可能性があります。何があってもインデックスには触れません。

これを回避するために、2つのテーブルで左結合(左外部結合とも呼ばれます)を使用できます。

SELECT a.column1
FROM table1 a LEFT JOIN table2 b ON a.id_key = b.fid_key
WHERE b.fid_key IS NULL;

外部結合では、結合されたテーブルの各レコードに一致するレコードが必要ではありません。したがって、上記の例では、table2に一致するものがない場合でも、table1からすべてのレコードを取得します。一致しないレコードの場合、データベースはNULLを返し、where句でそれをテストできます。これで、オプティマイザは2つのテーブルのid_keyフィールドのインデックスをスキャンできるようになり(存在すると仮定)、クエリがはるかに高速になります。

それで、まとめます。私はあなたの元のクエリをこう書き直します:

SELECT COUNT( a.c_id )
FROM contacts a
INNER JOIN mgroups b ON a.c_id = b.mg_id
LEFT JOIN mgroups_explicit c ON b.mg_id = c.me_mg_id
LEFT JOIN mgroups_exclude d ON a.c_id = d.mex_c_id
WHERE b.mg_id = '20'
AND a.site_id = '10'
AND a.c_tags LIKE '%tag1%'
AND d.mex_c_id IS NULL
GROUP BY c_id;
于 2011-02-01T10:58:35.443 に答える