5

管理者が Web からクロールされた投稿を含む複数のニュースレターを作成する必要があるプロジェクトがあります。

クロールが完了したら、テーブルに投稿を挿入し、ソースを識別するためpostsにそれらを割り当てます。feed_idこれはpostsテーブルの構造です(切り捨てられています):

CREATE TABLE `posts` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `feed_id` int(11) NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` timestamp NULL DEFAULT NULL,
  `identifier` varchar(255) DEFAULT NULL,
  `published` timestamp NULL DEFAULT NULL,
  `content` longtext,
  ...
  ...
  `is_unread` int(1) NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

すべての管理者 (ユーザー) は、1 つ以上の「フィード」にアクセスできます。そのため、ニュースレターの作成ページで、表示が許可されているフィードからの投稿のリストを表示したいと思います。また、ユーザーが以前にその投稿を選択した場合は、そのニュースレターの特定のカテゴリに投稿を配置するボタンを表示します。それを見せて、カテゴリから削除させてください。だから私もいくつかの他のテーブルを持っています: newsletters, categories, newsletter_post, category_post. それらの構造は次のとおりです。

newsletters:

CREATE TABLE `newsletters` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` timestamp NULL DEFAULT NULL,
  `sent_at` timestamp NULL DEFAULT NULL,
  `title` varchar(255) DEFAULT NULL,
  `date` date DEFAULT NULL,
  `topic_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

categories:

CREATE TABLE `categories` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `topic_id` int(11) NOT NULL,
  `title` varchar(255) DEFAULT NULL,
  `slug` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

newsletter_post:

CREATE TABLE `newsletter_post` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` timestamp NULL DEFAULT NULL,
  `newsletter_id` int(11) NOT NULL,
  `post_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

category_post:

CREATE TABLE `category_post` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` timestamp NULL DEFAULT NULL,
  `category_id` int(11) NOT NULL,
  `post_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

したがって、このクエリを使用して、許可されたフィードの投稿を検索し、投稿がこの特定のニュースレターの特定のカテゴリに含まれているかどうかを確認しています。

SELECT DISTINCT `posts`.`id`, `published`, `posts`.`title`, `posts`.`content`, `source_name`, `category_id`, `newsletter_id`, `link_href`, categories.title as category_title
FROM `posts`
LEFT JOIN `category_post` ON `posts`.`id` = `category_post`.`post_id`
LEFT JOIN `categories` ON `categories`.`id` = `category_post`.`category_id`
LEFT JOIN `newsletter_post` ON `posts`.`id` = `newsletter_post`.`post_id`
LEFT JOIN `newsletters` ON `newsletters`.`id` = `newsletter_post`.`newsletter_id`
WHERE `feed_id` IN (6, 7) ORDER BY `posts`.`published` DESC LIMIT 40 OFFSET 0

しかし、問題はこれが恐ろしく、最適化されていないことです。私postsのテーブルには毎月最大50,000行が含まれており、各行には平均で3〜10kbsのデータが含まれているため、クエリを実行しようとすると(管理者がニュースレターやページネーションなどを作成するために頻繁に実行します)mysqlが表示されますこのエラー: 結合する行が多すぎるなどで、ほとんどの場合、本当に遅いです。

これらすべてを 1 つのクエリで実行する理由は、結果を 1 つの json レスポンスにして、追加のリクエストを行わなくてもユーザーにすばやく表示できるようにするためです。

このクエリを実行したり、インデックスなどを使用したりするためのより良い方法があるかどうかを知りたいです。よろしくお願いします。

4

4 に答える 4

2

他の回答を完了するには、postsテーブルでこのタイプを変更することをお勧めします。

1) に変更feed_idint(4)ます。int(4)本当にあなたはフィード以上のものを持っていますか?
2)の代わりに に変更is_unreadします。これにより、質問で指定されたクエリが改善されない可能性がありますが、フィールド名によると、正しいタイプは.bitint(1)bit

この回答に対するもう1つの改善点はint(11)、数値またはIDフィールドにデフォルトを使用せず、タイプをより具体的に割り当てることです。小さいサイズの型を使用すると、インデックスも改善されます。int(4)フィールドID以上のものは必要ないと思います。

たとえば、int(3)列のインデックス作成とクエリは よりも高速ですint(11)

于 2013-01-03T14:13:33.600 に答える
1

:: に次のインデックス インデックスを作成してください

1) `post_id` in `category_post`
2) `post_id` in `newsletter_post`
于 2013-01-03T13:45:15.300 に答える