10

そのため、カウントを取得できるようにする必要がある2つのテーブルがあります。それらの 1 つはコンテンツを保持し、もう 1 つはそれとカテゴリ テーブルとの関係を保持します。DDl は次のとおりです。

CREATE TABLE content_en (
    id int(11) NOT NULL AUTO_INCREMENT,
    title varchar(100) DEFAULT NULL,
    uid int(11) DEFAULT NULL,
    date_added int(11) DEFAULT NULL,
    date_modified int(11) DEFAULT NULL,
    active tinyint(1) DEFAULT NULL,
    comment_count int(6) DEFAULT NULL,
    orderby tinyint(4) DEFAULT NULL,
    settings text,
    permalink varchar(255) DEFAULT NULL,
    code varchar(3) DEFAULT NULL,
    PRIMARY KEY (id),
    UNIQUE KEY id (id),
    UNIQUE KEY id_2 (id) USING BTREE,
    UNIQUE KEY combo (id,active) USING HASH,
    KEY code (code) USING BTREE
) ENGINE=MyISAM AUTO_INCREMENT=127126 DEFAULT CHARSET=utf8;

そして他のテーブルのために

CREATE TABLE content_page_categories (
    catid int(11) unsigned NOT NULL,
    itemid int(10) unsigned NOT NULL,
    main tinyint(1) DEFAULT NULL,
    KEY itemid (itemid),
    KEY catid (catid),
    KEY combo (catid,itemid) USING BTREE
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

私が実行しているクエリは次のとおりです。

SELECT count(*) 
FROM content_page_categories USE INDEX (combo) 
INNER JOIN content_en USE INDEX (combo) ON (id = itemid) 
WHERE catid = 1 AND active = 1 ;

両方のテーブルに 125,000 行あり、count クエリを十分に高速に実行できません。私が得た最良のタイミングは0.175で、これはこの量の行にとって恐ろしいものです。100 行の選択は 0.01 と同じ速さです。このクエリの 3 つまたは 4 つのバリエーションを試してみましたが、最終的にタイミングはほぼ同じです。また、USE INDEX を実行しないと、タイミングが 3 倍遅くなります。

次のことも試しました: SELECT COUNT( *) FROM content_page_categories INNER JOIN content_en ON id=itemid AND catid = 1 AND active = 1 WHERE 1

と :

SELECT SQL_CALC_FOUND_ROWS catid,content_en.* FROM content_page_categories INNER JOIN content_en ON (id=itemid) WHERE catid =1 AND active = 1 LIMIT 1; SELECT FOUND_ROWS();

索引の定義: content_en 0 PRIMARY 1 id A 125288 BTREE
content_en 0 id 1 id A 125288 BTREE
content_en 0 id_2 1 id A 125288 BTREE
content_en 0 combo 1 id A BTREE
content_en 0 combo 2 active A YES BTREE
content_en 1 code 1 code A 42 YES BTREE

content_page_categories 1 itemid 1 itemid A 96842 BTREE
content_page_categories 1 catid 1 catid A 10 BTREE
content_page_categories 1 combo 1 catid A 10 BTREE
content_page_categories 1 combo 2 itemid A 96842 BTREE

何か案は?

[編集]

これらのテーブルのサンプル データをここにアップロードしました

説明の結果:

mysql> explain SELECT count(*) FROM  content_page_categories USE INDEX (combo) I<br>
NNER JOIN content_en USE INDEX (combo) ON (id = itemid) WHERE  catid = 1 AND act<br>
ive = 1 ;

+----+-------------+-------------------------+-------+---------------+-------+---------+--------------------------+--------+--------------------------+
| id | select_type | table                   | type  | possible_keys | key   | key_len | ref                      | rows   | Extra                    |
+----+-------------+-------------------------+-------+---------------+-------+---------+--------------------------+--------+--------------------------+
|  1 | SIMPLE      | content_en              | index | combo         | combo | 6 | NULL                     | 125288 | Using where; Using index |
|  1 | SIMPLE      | content_page_categories | ref   | combo         | combo | 8 | const,mcms.content_en.id |      1 | Using where; Using index |
+----+-------------+-------------------------+-------+---------------+-------+---------+--------------------------+--------+--------------------------+
2 rows in set (0.00 sec)
4

5 に答える 5

5

数え切れないほどの記録があります。

より高速なソリューションが必要な場合は、集計データを保存する必要があります。

MySQL はマテリアライズド ビュー (または SQL Server の用語ではインデックス付きビュー) をサポートしていないため、自分で作成して維持する必要があります。

テーブルを作成します。

CREATE TABLE
        page_active_category
        (
        active INT NOT NULL,
        catid INT NOT NULL,
        cnt BIGINT NOT NULL,
        PRIMARY KEY
                (active, catid)
        ) ENGINE=InnoDB;

次に入力します。

INSERT
INTO    page_active_category
SELECT  active, catid, COUNT(*)
FROM    content_en
JOIN    content_page_categories
ON      itemid = id
GROUP BY
        active, catid

content_enこれで、またはでレコードを挿入、削除、または更新するたびにcontent_page_categories、 で適切なレコードを更新する必要がありますpage_active_category

これは、 と の両方で 2 つの単純なトリガーを使用して実行できcontent_enますcontent_page_categories

このように、元のクエリは次のように書き換えることができます。

SELECT  cnt
FROM    page_active_category
WHERE   active = 1
        AND catid = 1

これは単一の主キー検索であり、したがって瞬時です。

于 2013-07-09T17:32:59.320 に答える
1

問題は、content_en の「アクティブ」列です。明らかに、特定のカテゴリ (アクティブかどうか) に関連するコンテンツ レコードの数を知る必要がある場合は、次のようにする必要があります。

SELECT count(1)
FROM content_page_categories
WHERE catid = 1;

「アクティブ」フラグを読み取るためだけにすべての content_en レコードに参加しなければならないことが、このクエリの速度を低下させている原因です。

content_page_categories に「active」を追加し、content_en の関連する値のコピーにすることをお勧めします。トリガーまたはコードを使用して、この列を最新の状態に保つことができます。次に、コンボ インデックスを次のように変更できます。

KEY combo (catid,active,itemid)

クエリを次のように書き換えます。

SELECT count(1)
FROM content_page_categories USE INDEX (combo)
WHERE catid = 1 AND active = 1;

また、MyISAM の代わりに InnoDB テーブルを使用すると、よりうまくいく可能性があります。InnoDB の設定を必ず調整してください: http://www.mysqlperformanceblog.com/2007/11/01/innodb-performance-optimization-basics/

于 2013-07-09T16:57:55.123 に答える
0

あなたのデータを設定した私にとって、結合クエリは content_page_categories から選択するよりも 50 倍の時間がかかりました。

データで次のことを行うことで、カテゴリ テーブルから選択するよりも約 10 倍遅いパフォーマンスを達成できました。

私は Straight_join を使用しました

    SELECT straight_join count(*) 
    FROM content_en 
    INNER JOIN content_page_categories use index (combo) 
     ON (id = itemid) 
    WHERE catid = 1 AND active = 1 ;

および次のテーブル構造 (わずかに変更):

 CREATE TABLE `content_en` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `title` varchar(100) DEFAULT NULL,
 `uid` int(11) DEFAULT NULL,
 `date_added` int(11) DEFAULT NULL,
 `date_modified` int(11) DEFAULT NULL,
 `active` tinyint(1) DEFAULT NULL,
 `comment_count` int(6) DEFAULT NULL,
 `orderby` tinyint(4) DEFAULT NULL,
 `settings` text,
 `permalink` varchar(255) DEFAULT NULL,
 `code` varchar(3) DEFAULT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `id` (`id`),
 KEY `test_con_1` (`active`) USING HASH,
 KEY `combo` (`id`,`active`) USING HASH
 ENGINE=MyISAM AUTO_INCREMENT=127126 DEFAULT CHARSET=utf8

と:

CREATE TABLE `content_page_categories` (
`catid` int(11) unsigned NOT NULL,
`itemid` int(10) unsigned NOT NULL,
`main` tinyint(1) DEFAULT NULL,
KEY `itemid` (`itemid`),
KEY `catid` (`catid`),
KEY `test_cat_1` (`catid`) USING HASH,
KEY `test_cat_2` (`itemid`) USING HASH,
KEY `combo` (`itemid`,`catid`) USING HASH
ENGINE=MyISAM DEFAULT CHARSET=utf8

これを超えるには、ビュー、フラット化された構造、または別のタイプのルックアップ フィールドが必要になると思います (別のポスターで説明されているように、他のテーブルに行を入力するトリガーのように)。

編集:

また、なぜ/いつ注意する必要があるかについてのこのまともな投稿を指摘する必要がありStraight_Joinます: MySQLでSTRAIGHT_JOINを使用する場合

使用する場合は、責任を持って使用してください。

于 2013-07-09T17:37:46.207 に答える