【问题标题】:MySQL Query does not use index in table joinMySQL Query 在表连接中不使用索引
【发布时间】:2011-01-20 23:46:26
【问题描述】:

我正在尝试列出特定图书作者的所有 book_sales 信息。所以我有一个查询,它没有使用索引来查找记录。

以下是我的表格结构:

-- Table structure for table `books`

CREATE TABLE IF NOT EXISTS `books` (
  `book_id` int(11) NOT NULL auto_increment,
  `author_id` int(11) unsigned NOT NULL,
  `book_type_id` int(11) NOT NULL,
  `book_title` varchar(50) NOT NULL,
  `book_price` smallint(4) NOT NULL,
  `in_stock` char(1) NOT NULL,
  PRIMARY KEY  (`book_id`),
  KEY `book_type_id` (`book_type_id`),
  KEY `author_id` (`author_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

-- Dumping data for table `books`

INSERT INTO `books` (`book_id`, `author_id`, `book_type_id`, `book_title`, `book_price`, `in_stock`) VALUES
(1, 1, 1, 'My Book 1', 10, 'y'),
(2, 2, 1, 'My Book 2', 20, 'n'),
(3, 1, 2, 'My Book 3', 30, 'y'),
(4, 3, 3, 'My Book 4', 40, 'y'),
(5, 4, 2, 'My Book 5', 50, 'n'),
(6, 1, 1, 'My Book 6', 60, 'y'),
(7, 5, 3, 'My Book 7', 70, 'n'),
(8, 6, 2, 'My Book 8', 80, 'n'),
(9, 7, 1, 'My Book 9', 90, 'y'),
(10, 8, 3, 'My Book 10', 100, 'n');

-- Table structure for table `book_sales`

CREATE TABLE IF NOT EXISTS `book_sales` (
  `sale_id` int(11) NOT NULL auto_increment,
  `book_id` int(11) NOT NULL,
  `sale_amount` decimal(8,2) NOT NULL default '0.00',
  `time` datetime NOT NULL default '0000-00-00 00:00:00',
  `price` smallint(8) NOT NULL,
  PRIMARY KEY  (`sale_id`),
  KEY `book_id` (`book_id`),
  KEY `price` (`price`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

-- Dumping data for table `book_sales`

INSERT INTO `book_sales` (`sale_id`, `book_id`, `sale_amount`, `time`, `price`) VALUES
(1, 1, '10.00', '2010-02-23 10:00:00', 20),
(2, 1, '20.00', '2010-02-24 11:00:00', 20);

我的查询:

SELECT sale_amount, price
FROM book_sales
INNER JOIN books ON book_sales.book_id = books.book_id
WHERE books.author_id =1

上面的解释告诉我:

id   select_type    table        type        possible_keys      key      key_len    ref     rows      Extra
1   SIMPLE          books       ref     PRIMARY,author_id   author_id   4         const    3        Using index
1   SIMPLE          book_sales  ALL     book_id             NULL        NULL      NULL     2        Using where

很明显,book_sales 没有使用密钥“book_id”,尽管我有它。我该怎么做才能使 book_sales 表使用索引?

谢谢。

根据建议进行的编辑(但结果是他们仍然不使用索引):

//Does not use the index in book_sales table
EXPLAIN SELECT sale_amount, price
FROM books, book_sales
FORCE INDEX ( book_id ) 
WHERE book_sales.book_id = books.book_id
AND books.author_id =1

//Does not use the index in book_sales table
EXPLAIN SELECT sale_amount, price
FROM book_sales, books
WHERE books.author_id = 1
AND book_sales.book_id = books.book_id

如何强制只有 2 行的 book_sale 表使用索引?谢谢。

【问题讨论】:

    标签: mysql join indexing


    【解决方案1】:

    正如您在 EXPLAIN 中看到的,“book_id”被列为可能的键。如果 MySQL 不使用它,只是优化器认为它不会加快查询速度。如果“book_sales”只有 2 行,并且 100% 的行共享相同的“book_id”,这是正确的。顺便说一句,它被称为基数。 How to Avoid Table Scans(MySQL手册)

    尝试用更多行填充它,您应该会看到 MySQL 将使用索引进行连接。

    编辑:查询

    SELECT sale_amount, price
    FROM books, book_sales
    FORCE INDEX ( book_id ) 
    WHERE book_sales.book_id = books.book_id
    AND books.author_id =1
    

    ...在这种情况下也不会起作用,因为优化器仍然认识到读取索引是次优的并切换表顺序以避免这样做。您可以使用STRAIGHT_JOIN 强制表格顺序。然而,这有点 hack,因为它强制 MySQL 以一种不是最好的方式执行查询。

          EXPLAIN
           SELECT sale_amount, price
             FROM books
    STRAIGHT_JOIN book_sales FORCE INDEX (book_id) ON book_sales.book_id = books.book_id
            WHERE books.author_id = 1
    

    【讨论】:

    • 没错。通常,如果您选择超过 25% 的表格,索引会减慢您的速度。
    • @Josh Davis 感谢您的回复。正如您所建议的那样,我用额外的数据填充了表格,当我尝试了最初与我一起使用的查询时,它起作用了! book_sales 表使用的是 book_id 键!!!终于解脱了!!!所以这里的罪魁祸首是mysql慢查询日志。我打开了它并记录了所有不使用索引的查询,这个查询出现在日志中,我的战斗就从它开始了。那么有什么办法可以禁止此日志显示上述类型的查询?非常感谢!
    • AFAIK,您无法过滤哪些查询进入慢查询日志,但您应该能够通过使用 FORCE INDEX 强制优化器避免表扫描。 dev.mysql.com/doc/refman/5.5/en/index-hints.html
    • @Josh Davis 我参考了您发布的链接,我认为我可以强制使用索引并根据需要获取我的查询,但它仍然无法正常工作并且表不使用索引。因此,我编辑了我的原始帖子以包含我尝试过的查询。也许如果我做错了什么,请向我建议适用的更正。谢谢。
    • 是的,那是因为优化器仍然通过切换表来击败您。我已经用一种解决方法更新了我的答案。
    【解决方案2】:

    试试这个

    SELECT sale_amount, price
    FROM book_sales,books
    LEFT JOIN books ON(book_sales.book_id = books.book_id)
    WHERE books.author_id =1
    

    【讨论】:

    • 感谢您的回复。我试过你的代码。得到“#1066 - 不是唯一的表/别名:'books'”错误。所以我删除了 FROM 子句中的 ",books"。做了解释,但它仍然不使用索引。我们还能做些什么来解决这个问题?
    • Hy Devner 请听从 Josh Davis 的回答
    • 我做到了,我访问了他发布的链接。我看到一个名为“--max-seeks-for-key=1000”的选项。它说:“使用 --max-seeks-for-key=1000 选项启动 mysqld 或使用 SET max_seeks_for_key=1000 告诉优化器假设没有键扫描会导致超过 1,000 次键搜索。”我不确定这到底意味着什么。你能解释一下吗?谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-09
    • 1970-01-01
    • 2012-10-03
    • 1970-01-01
    • 2021-12-20
    相关资源
    最近更新 更多