views:

402

answers:

2

Hi,

I am trying to list all the book_sales information for a particular book author. So I have a query and it's not using Index to lookup records.

The following is my tables structure:

-- 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);

My Query:

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

An EXPLAIN on the above, shows me:

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

Clearly, book_sales is not using the key 'book_id', although I have it. What can I do to make the book_sales table use the Index?

Thank you.

Edits done based on suggestions (but the result is they still do not use index):

//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

How to force the book_sale table with just 2 rows, to use the index ? Thank you.

A: 

Try this

SELECT sale_amount, price
FROM book_sales,books
LEFT JOIN books ON(book_sales.book_id = books.book_id)
WHERE books.author_id =1
streetparade
Thank you for the reply. I tried your code. Got the "#1066 - Not unique table/alias: 'books'" Error. So I removed ",books" in the FROM clause. Did an explain and still it does not use the index. Anything else that we can do to fix this?
Devner
Hy Devner Please follow the answer of Josh Davis
streetparade
@streetparade I did and I visited the link that he posted. I see an option called "--max-seeks-for-key=1000". It says: "Start mysqld with the --max-seeks-for-key=1000 option or use SET max_seeks_for_key=1000 to tell the optimizer to assume that no key scan causes more than 1,000 key seeks." I am not sure what this really means. Can you please throw any light on this? Thank you.
Devner
+3  A: 

As you can see in the EXPLAIN, "book_id" is listed as a possible key. If MySQL doesn't use it, it's just that the optimizer doesn't think it would speed up the query. Which is true if "book_sales" only has 2 rows, and 100% of those rows share the same "book_id". It's called cardinality btw. How to Avoid Table Scans (MySQL Manual)

Try filling it with more rows and you should see that MySQL will use an index for the join.

Edit: the query

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

...will not work either in that case because the optimizer still recognizes that reading the index is suboptimal and switches the table order to avoid doing so. You can force the table order by using STRAIGHT_JOIN. This is, however, a bit of a hack because it forces MySQL to execute the query in a way that is not the best.

      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
Josh Davis
Exactly. Typically an index will slow you down if you are selecting more than 25% of the table.
Phil Wallach
+1 Thats true and valid in all cases.
streetparade
@Josh Davis Thank you for the reply. As you suggested, I filled up the table with extra data and when I tried out the query that I had with me initially, it worked! The book_sales table was using the book_id key!!! Relief finally!!! So here the culprit was the mysql slow query log. I had it turned on and was logging all queries which weren't using index and this query showed up in the log and my battle started with it. So is there any way I can suppress this log from showing the queries of above kind? Thanks much!
Devner
AFAIK, you cannot filter which queries go to the slow query log, but you should be able to force the optimizer to avoid the table scan by using FORCE INDEX. http://dev.mysql.com/doc/refman/5.5/en/index-hints.html
Josh Davis
@Josh Davis I referred the link that you posted and I thought I would be able to Force the index to be used and get my query as desired but it still does not work and the table does not use the index. As such, I edited my original post to include the queries that I have tried. Maybe if I am doing something wrong, please suggest me the applicable correction. Thank you.
Devner
Yeah, that's because the optimizer still beats you by switching the tables around. I've updated my answer with a way around that.
Josh Davis
@Josh Davis Awesome! You have some precise code right there. With the new code that you gave, the book_sales is actually using the book_id index, even though it has 2 records. As you said, maybe that way of coding may be a hack but my feeling is that using so is causing index to be used the way it should be, which means less table scans and less load on the server. So Faster Server = Happy Users :) . I think that would work perfect for me if there are no side effects for this. Hope I am right. Appreciate all your help. Thank you so much!!!
Devner