web-dev-qa-db-ja.com

2つの範囲条件でクエリを最適化する

私はこれに似た構造を持っています:

CREATE TABLE `author` (
  `id` int(11) unsigned NOT NULL auto_increment,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;

CREATE TABLE `book` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `author_id` int(11) unsigned NOT NULL,
  `org` int(11) unsigned NOT NULL,
  `country` char(3) NOT NULL,
  `publish_date` date NOT NULL,
  `price` decimal(6,2) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `author_id` (`author_id`),
  KEY `publish_date` (`publish_date`),
  KEY `i0` (`country`, `org`, `author_id`, `price`, `publish_date`),
  KEY `i1` (`country`, `org`, `author_id`, `publish_date`, `price`)
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;

これが SQL Fiddle です

だから私は2つの範囲条件でクエリを実行しようとしています

SELECT
  id as author_id,
  (SELECT COUNT(DISTINCT `book`.`id`)+1 
   FROM `book` 
   WHERE 
      `book`.`org` = 1
      AND `book`.`country` = 'USA' 
      AND `book`.`publish_date` BETWEEN '2010-04-30' AND '2011-04-30'
      AND `book`.`author_id` = `author`.`id`
      AND `book`.`price` < 50
  ) AS `books_under_fifty`
FROM `author` 
ORDER BY books_under_fifty desc;

しかし、オプティマイザは私のインデックスの一部のみを使用します:const,const,db_9_6349e2.author.idからi0

それを最適化する方法はありますか?

3
Todor

サブクエリをLEFT JOINおよびGROUP BYに変更できます。

SELECT
    author.id as author_id, (count(distinct book.id) + 1) AS books_under_fifty
FROM
    author
    LEFT JOIN book ON
          book.author_id = author.id
      AND book.org = 1
      AND book.country = 'USA' 
      AND book.publish_date BETWEEN '2010-04-30' and '2011-04-30'
      AND book.price < 50
GROUP BY
    author.id
ORDER BY 
     books_under_fifty desc, author_id;

...しかし、実行計画に関しては実際には何の違いもありません。

dbfiddleですべてを確認できます here

だから、私は恐らく答えはであり、それ以上最適化することはできません。いずれにしても、私が使用したシミュレーションではなく、実際のデータでテストすることをお勧めします。

ノート:

  1. 2番目のORDER BY式を追加して、順序が完全に確定的であることを確認します。
  2. 最初にMariaDBではなく、MariaDBで確認しました。 MySQLは2つの異なるプランを使用しますが、それでも選択の幅が狭くなります( http://rextester.com/JDHP57216 で確認してください)。

LEFT JOINのバリエーションも確認できます。

SELECT
    author.id as author_id, coalesce(ccc, 1) AS books_under_fifty
FROM
    author
    LEFT JOIN 
    (SELECT 
        author_id, count(distinct book.id) + 1 AS ccc
    FROM
        book 
    WHERE
            book.org = 1
        AND book.country = 'USA' 
        AND book.publish_date BETWEEN '2010-04-30' and '2011-04-30'
        AND book.price < 50
     GROUP BY
          author_id
     ) AS q0
     ON q0.author_id = author.id
ORDER BY 
     books_under_fifty desc, author_id;

しかし、ここでも、MariaDBが何の利点も得ていないようです。

dbfiddle ここ


別のオープンソースデータベース(PostgreSQL 9.6)は、はるかに洗練された方法で処理を行うことができ、実行計画をはるかに優れたものにすることができます...(そしておそらく、より高速ですが、MariaDBはタイミングを取得しませんでした)。

PostgreSQLはLEFT JOINをより高速に動作させることができます(ほとんどの場合、各実行のランダム性に依存しますが)。

dbfiddle ここ

1
joanolo