I'm building an ecommerce website for fun with three main components: frontend in Angular, MySQL database and a PHP backend that will function as a REST API.
The inventory database consists of products of which there are many variants (Yes, I've seen many questions on StackExchange discussing these kind of issues but I havent found a satisfying soultion yet). After reading several horror stories of using an EAV approach, Im avoiding that.
I think I have made a database solution that works, but Im uncertain to how I should query the data. As there are several many-to-many relationships a query for 1 product and all its variants can return several rows where only a couple columns contain "new" data.
(TL:DR) Main question: When querying a product for all its variants (colors, sizes of available colors and quantity of a color/size variant), what is most efficient:
Perform 1 database query returning many rows where I would need to build an associative array in PHP, discarding "duplicate" data?
Perform several queries when retrieving all variants of a product?
Or is there a flaw in the database design which is the cause of these issues?
Database schema:
CREATE TABLE IF NOT EXISTS `products` (
`productID` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
`longDescription` TEXT,
`shortDescription` VARCHAR(1000),
PRIMARY KEY (`productID`)
);
INSERT INTO `products` (`productID`, `name`, `longDescription`, `shortDescription`) VALUES
(1, 'A shirt', 'Long description of this product', 'shortDesc of shirt');
CREATE TABLE IF NOT EXISTS `productpricing` (
`productID` INT(11) NOT NULL,
`startDate` TIMESTAMP NOT NULL DEFAULT '1971-01-01 00:00:00',
`endDate` TIMESTAMP NOT NULL DEFAULT '2099-01-01 00:00:00',
`price` DECIMAL(10, 2) NOT NULL,
PRIMARY KEY(`productID`, `endDate`),
FOREIGN KEY (`productID`) REFERENCES products(`productID`)
);
INSERT INTO `productpricing` (`productID`, `startDate`, `endDate`, `price`) VALUES
(1, '1971-01-01 00:00:00', '2099-01-01 00:00:00', 309.99);
CREATE TABLE IF NOT EXISTS `categories` (
`categoryID` INT(11) NOT NULL AUTO_INCREMENT,
`categoryName` VARCHAR(100) NOT NULL,
PRIMARY KEY (`categoryID`)
);
INSERT INTO `categories` (`categoryID`, `categoryName`) VALUES
(1, 'Test Category');
CREATE TABLE IF NOT EXISTS `sizes` (
`sizeID` INT(11) NOT NULL,
`size` INT NOT NULL,
PRIMARY KEY (`sizeID`)
);
INSERT INTO `sizes` (`sizeID`, `size`) VALUES
(1, 50),
(2, 56),
(3, 62),
(4, 68),
(5, 74),
(6, 80),
(7, 86);
CREATE TABLE IF NOT EXISTS `colors` (
`colorID` INT(11) NOT NULL,
`color` VARCHAR(100) NOT NULL,
PRIMARY KEY (`colorID`)
);
INSERT INTO `colors` (`colorID`, `color`) VALUES
(1, "Red"),
(2, "White"),
(3, "Blue"),
(4, "Purple");
CREATE TABLE IF NOT EXISTS `product_variants` (
`productvariantID` INT(11) NOT NULL,
`productID` INT(11) NOT NULL,
`categoryID` INT(11) NOT NULL,
`colorID` INT(11) NOT NULL,
`sizeID` INT(11) NOT NULL,
`sku` VARCHAR(50) NOT NULL UNIQUE,
`quantity` INT(11) NOT NULL,
`isActive` BOOLEAN NOT NULL DEFAULT 0,
PRIMARY KEY (`productvariantID`),
UNIQUE (`productID`, `colorID`, `sizeID`),
FOREIGN KEY (`productID`) REFERENCES products(`productID`),
FOREIGN KEY (`categoryID`) REFERENCES categories(`categoryID`),
FOREIGN KEY (`colorID`) REFERENCES colors(`colorID`),
FOREIGN KEY (`sizeID`) REFERENCES sizes(`sizeID`)
);
INSERT INTO `product_variants` (`productvariantID`, `productID`, `categoryID`, `colorID`, `sizeID`, `sku`, `quantity`, `isActive`) VALUES
(1, 1, 1, 2, 1, 'clalb121', 2, 1),
(2, 1, 1, 3, 2, 'clalb132', 1, 1),
(3, 1, 1, 2, 2, 'clalb122', 5, 1);
CREATE TABLE IF NOT EXISTS `images` (
`imageID` INT(11) NOT NULL AUTO_INCREMENT,
`imageFilename` VARCHAR(100) NOT NULL,
PRIMARY KEY(`imageID`)
);
INSERT INTO `images` (`imageID`, `imageFilename`) VALUES
(1, 'shirtwhite1.jpg'),
(2, 'shirtwhite2.jpg'),
(3, 'shirtblue1.jpg'),
(4, 'shirtblue2.jpg');
CREATE TABLE IF NOT EXISTS `product_variant_images` (
`productvariantID` INT(11) NOT NULL,
`imageID` INT(11) NOT NULL,
FOREIGN KEY(`productvariantID`) REFERENCES product_variants(`productvariantID`),
FOREIGN KEY(`imageID`) REFERENCES images(`imageID`)
);
INSERT INTO `product_variant_images` (`productvariantID`, `imageID`) VALUES
(1, 1),
(1, 2),
(2, 3),
(2, 4),
(3, 1),
(3, 2);
(Im going to remove categoryID from the product_variants table as different variants of the same product does not have different categories.)
Example of querying 1 product for all its variants (In one query):
SELECT
p.productID,
p.name,
p.longDescription,
p.shortDescription,
colors.color,
sizes.size,
pvar.quantity,
pprice.price,
images.imageFilename
FROM products as p
JOIN product_variants as pvar
ON p.productID = pvar.productID
JOIN productpricing as pprice
ON pprice.productID = p.productID
JOIN colors
ON colors.colorID = pvar.colorID
JOIN sizes
ON sizes.sizeID = pvar.sizeID
JOIN product_variant_images as pvari
ON pvari.productvariantID = pvar.productvariantID
JOIN images
ON images.imageID = pvari.imageID
WHERE p.productID = 1 AND pvar.isActive = 1 AND NOW() BETWEEN pprice.startDate AND pprice.endDate;
Example return set:
# productID, name, longDescription, shortDescription, color, size, quantity, price, imageFilename
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', 'White', '50', '2', '309.99', 'shirtwhite1.jpg'
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', 'White', '50', '2', '309.99', 'shirtwhite2.jpg'
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', 'White', '56', '5', '309.99', 'shirtwhite1.jpg'
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', 'White', '56', '5', '309.99', 'shirtwhite2.jpg'
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', 'Blue', '56', '1', '309.99', 'shirtblue1.jpg'
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', 'Blue', '56', '1', '309.99', 'shirtblue2.jpg'
As can be seen in the result set the white variant (of one size) of the shirt is associated with two images, therefore there are two records with only imageFilename containing "new data".
Example of using several queries:
SELECT
p.productID,
p.name,
p.longDescription,
p.shortDescription,
pprice.price
FROM products as p
JOIN productpricing as pprice
ON pprice.productID = p.productID
WHERE p.productID = 1 AND NOW() BETWEEN pprice.startDate AND pprice.endDate;
SELECT
c.color,
s.size,
pvar.quantity
FROM product_variants as pvar
JOIN colors AS c
ON c.colorID = pvar.colorID
JOIN sizes as s
ON s.sizeID = pvar.sizeID
WHERE pvar.productID = 1;
SELECT DISTINCT
c.color,
i.imageFilename
FROM images AS i
JOIN product_variant_images as pvari
ON pvari.imageID = i.imageID
JOIN product_variants as pvar
ON pvar.productvariantID = pvari.productvariantID
JOIN colors AS c
ON c.colorID = pvar.colorID
WHERE pvar.productID = 1;
Several queries result sets:
# productID, name, longDescription, shortDescription, price
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', '309.99'
# color, size, quantity
'White', '50', '2'
'Blue', '56', '1'
'White', '56', '5'
# color, imageFilename
'White', 'shirtwhite1.jpg'
'White', 'shirtwhite2.jpg'
'Blue', 'shirtblue1.jpg'
'Blue', 'shirtblue2.jpg'
Any advice regarding the database design/queries is much appreciated!
Related
I have two relational tables, and I would like to filter data using IF condition. The problem is that using LEFT JOIN I got records that cannot be grouped.
The tables that I have are:
calendar
bookers
The first table consists of lessons that can be booked by more people, and the second table contains data who booked each lesson. The IF condition that I would like to implement is: return '2' if lesson is booked by specific user, return '1' if lesson is booked, but by another user, and return '0' if lesson is not booked.
What I would like to get according to above tables is given in the figure below.
Expected result
But, when I use LEFT JOIN to link those tables, I got record for every user that booked specific lesson.
SELECT calendar.id, calendarId, lessonType, description,
CASE
WHEN bookedBy then IF(bookedBy = 8, '2', '1')
ELSE '0'
END AS bb,
(select count(bookedBy) from bookers where calendar.id = bookers.lessonId) as nOfBookers
FROM calendar
LEFT JOIN bookers ON calendar.id = bookers.lessonId
WHERE `calendarId`= 180
Without the LEFT JOIN (fiddle), counts are shown properly, but I cannot include IF condition, because the table bookers is not defined.
I would appreciate any help. Thank you very much in advance.
Here is the Fiddle.
CREATE TABLE `calendar` (
`id` int(11) NOT NULL,
`calendarId` varchar(50) NOT NULL,
`lessonType` varchar(255) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `calendar`
(`id`, `calendarId`, `lessonType`, `description`)
VALUES
(1, '180', 'A', ''),
(2, '180', 'A', ''),
(3, '180', 'A', ''),
(4, '180', 'B', ''),
(5, '180', 'B', ''),
(6, '180', 'B', ''),
(7, '180', 'B', ''),
(8, '180', 'B', ''),
(9, '180', 'B', '');
CREATE TABLE `bookers` (
`id` int(11) NOT NULL,
`lessonId` int(11) DEFAULT NULL,
`bookedBy` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
--
-- Dumping data for table `bookers`
--
INSERT INTO `bookers` (`id`, `lessonId`, `bookedBy`) VALUES
(4, 1, 8),
(5, 2, 8),
(6, 2, 28),
(7, 2, 17),
(8, 3, 11);
--
-- Indexes for dumped tables
--
ALTER TABLE `calendar`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `id` (`id`);
--
-- Indexes for table `bookers`
--
ALTER TABLE `bookers`
ADD PRIMARY KEY (`id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `bookers`
--
ALTER TABLE `bookers`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=9;
COMMIT;
select version();
Try this:
SELECT id, calendarid, lessontype, description,
CASE WHEN FIND_IN_SET(8,vbb)>0 THEN 2
WHEN vbb IS NOT NULL THEN 1
ELSE 0 END AS bb,
nOfBookers
FROM
(SELECT c.id, calendarId, lessonType, GROUP_CONCAT(bookedby) AS vbb, description,
(SELECT COUNT(bookedby) FROM bookers WHERE c.id = bookers.lessonId) AS nOfBookers
FROM calendar c
LEFT JOIN bookers b ON c.id = b.lessonId
WHERE `calendarId`= 180
GROUP BY c.id, calendarId, lessonType, description) A;
In addition to your original LEFT JOIN attempt, I've added GROUP_CONCAT(bookedby) AS vbb which will return a comma separated bookedby value; which is 17,28,8. After that, I make the query as a sub-query and do CASE expression with FIND_IN_SET function on vbb to look for specific bookedby.
Here's an update fiddle: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=0933e9fc3cb7445311c34c6705d11637
i stumbled the following problem that i cannot solve
i have the following table in the database
CREATE TABLE `departments` (
`Company` int(11) NOT NULL,
`Department` varchar(32) NOT NULL,
`DepartmentName` varchar(40) NOT NULL,
`parentDepartment` varchar(32) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `departments` (`Company`, `Department`, `DepartmentName`, `parentDepartment`) VALUES
(1, '1', 'Company1 Ltd', '1'),
(1, '101', 'Information Technology', '1'),
(1, '10101', 'Hardware', '101'),
(1, '10102', 'Software', '101'),
(1, '102', 'Sales Department', '1'),
(1, '10201', 'Travelling', '101');
COMMIT;
basically its a list of departments in the company. Each department can be "nested" under another using the "parentDepartment" field.
Department field is the code of the department. But the numbering is irrelevant to the "structure".
what i want to achieve can be viewed in the picture bellow.
The question is how to sort this table out and keep the relationships visible?
Thank you
Try this:
SELECT *
FROM departments
ORDER BY department;
I am learning MySQL and I currently do not understand how to do something.
I have two tables and I want to display some stuff out of it, it's pretty hard to explain so I'd rather show you.
These are my tables:
CREATE TABLE IF NOT EXISTS `proprietate` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`serie_buletin` varchar(8) NOT NULL,
`cnp` bigint(20) NOT NULL,
`nr_vehicul` int(11) NOT NULL,
`data_cumpararii` date NOT NULL,
`pret` int(11) NOT NULL,
`id_persoana` int(11) NOT NULL,
PRIMARY KEY (id),
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=16 ;
CREATE TABLE IF NOT EXISTS `vehicul` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`nr_vehicul` int(11) NOT NULL,
`marca` varchar(30) NOT NULL,
`id_marca` int(11) NOT NULL,
`tip` varchar(15) NOT NULL,
`culoare` varchar(15) NOT NULL,
`capacitate_cilindrica` int(11) NOT NULL,
`id_proprietate` int(11) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (id_proprietate) REFERENCES proprietate(id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=16 ;
And these are my values inside my tables:
INSERT INTO `proprietate` (`id`, `serie_buletin`, `cnp`, `nr_vehicul`, `data_cumpararii`, `pret`, `id_persoana`) VALUES
(1, 'AK162332', 2006036035087, 4, '2014-05-01', 35000, 1),
(2, 'AK162332', 2006036035087, 10, '2014-05-02', 90000, 2),
(3, 'AK176233', 6548751520125, 2, '2014-05-03', 55000, 3),
(4, 'BZ257743', 6548751520125, 2, '2014-05-04', 25000, 4),
(5, 'BZ257743', 2006036035087, 15, '2014-05-05', 63000, 5),
(6, 'DC456542', 2003564784513, 7, '2014-05-06', 30000, 6),
(7, 'EN654872', 2012654879521, 6, '2014-05-07', 50000, 7);
INSERT INTO `vehicul` (`id`, `nr_vehicul`, `marca`, `id_marca`, `tip`, `culoare`, `capacitate_cilindrica`, `id_proprietate`) VALUES
(1, 4, 'Mercedes', 1, 'CLK 350', 'negru', 3500, 1),
(2, 10, 'Mercedes', 1, 'S 500', 'silver', 5000, 2),
(3, 2, 'Mercedes', 1, 'ML 550', 'alb', 5500, 3),
(4, 2, 'BMW', 2, '325', 'galben', 2500, 4),
(5, 15, 'BMW', 2, 'X5', 'negru', 3500, 5),
(6, 7, 'Audi', 3, 'R5', 'mov', 5000, 6),
(7, 6, 'Audi', 3, 'Q5', 'metalic', 3000, 7);
What I want to display is:
marca | nr_vehicul | average_price
Audi | 13 | 40000
BMW | 17 | 44000
Mercedes | 16 | 60000
How can I do that? So far I have managed to display the first two columns but I have no idea how to reference the first table in the second and calculate the average price.
This is what I have so far:
SELECT marca, SUM(nr_vehicul) AS nr_vehicul FROM vehicul GROUP BY marca
Can anyone help me please?
You should join your tables to get combined information from both of them:
SELECT marca, SUM(vehicul.nr_vehicul) AS nr_vehicul, avg(pret) as pret
FROM vehicul
LEFT OUTER JOIN proprietate on (id_proprietate = proprietate.id)
GROUP BY marca;
see this sql fiddle session for the output.
First you select the data (column names with appropriate functions used) you need: marca, SUM(vehicul.nr_vehicul), AVG(pret), then you construct the joined structure from where mysql should retrieve these informations: vehicul, proprietate.
For this structure you need primarily the vehicul table, by which you will group the result set. You want to join the proprietate table to the vehicul table properly, to make sure the correct data structure is created. Since you have foreign key from one table to the other, the easiest way to do it is to use that key: LEFT OUTER JOIN proprietate on (id_proprietate = proprietate.id).
For more information on understanding the different JOIN types, please see this article by Craig Buckler.
$query = "SELECT type, AVG(pret) FROM vehicul GROUP BY marca";
$result = mysql_query($query) or die(mysql_error());
// Print out result
while($row = mysql_fetch_array($result)){
echo "The average price of ". $row['type']. " is $".$row['AVG(price)'];}</code>
should return the average price per marca
Following is a dump of the tables and data needed to answer understand the system:-
The system consists of tutors and classes.
The data in the table All_Tag_Relations stores tag relations for each tutor registered and each class created by a tutor. The tag relations are used for searching classes.
CREATE TABLE IF NOT EXISTS `Tags` (
`id_tag` int(10) unsigned NOT NULL auto_increment,
`tag` varchar(255) default NULL,
PRIMARY KEY (`id_tag`),
UNIQUE KEY `tag` (`tag`),
KEY `id_tag` (`id_tag`),
KEY `tag_2` (`tag`),
KEY `tag_3` (`tag`),
KEY `tag_4` (`tag`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `Tags` (`id_tag`, `tag`) VALUES
(1, 'Sandeepan'),
(2, 'Nath'),
(3, 'first'),
(4, 'class'),
(5, 'new'),
(6, 'Bob'),
(7, 'Cratchit');
CREATE TABLE IF NOT EXISTS `All_Tag_Relations` (
`id_tag` int(10) unsigned NOT NULL default '0',
`id_tutor` int(10) default NULL,
`id_wc` int(10) unsigned default NULL,
KEY `All_Tag_Relations_FKIndex1` (`id_tag`),
KEY `id_wc` (`id_wc`),
KEY `id_tag` (`id_tag`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `All_Tag_Relations` (`id_tag`, `id_tutor`, `id_wc`) VALUES
(1, 1, NULL),
(2, 1, NULL),
(3, 1, 1),
(4, 1, 1),
(6, 2, NULL),
(7, 2, NULL),
(5, 2, 2),
(4, 2, 2),
(8, 1, 3),
(9, 1, 3);
Following is my query:-
This query searches for "first class" (tag for first = 3 and for class = 4, in Tags table) and returns all those classes such that both the terms first and class are present in the class name.
SELECT wtagrels.id_wc,SUM(DISTINCT( wtagrels.id_tag =3)) AS
key_1_total_matches,
SUM(DISTINCT( wtagrels.id_tag =4)) AS
key_2_total_matches
FROM all_tag_relations AS wtagrels
WHERE ( wtagrels.id_tag =3
OR wtagrels.id_tag =4 )
GROUP BY wtagrels.id_wc
HAVING key_1_total_matches = 1
AND key_2_total_matches = 1
LIMIT 0, 20
And it returns the class with id_wc = 1.
But, I want the search to show all those classes such that all the search terms are present in the class name or its tutor name
So that searching "Sandeepan class" (wtagrels.id_tag = 1,4) or "Sandeepan Nath" also returns the class with id_wc=1. And Searching. Searching "Bob First" should not return any classes.
Please modify the above query or suggest a new query, if possible using MyIsam - fulltext search, but somehow help me get the result.
I think this query would help you:
SET #tag1 = 1, #tag2 = 4; -- Setting some user variables to see where the ids go. (you can put the values in the query)
SELECT wtagrels.id_wc,
SUM(DISTINCT( wtagrels.id_tag =#tag1 OR wtagrels.id_tutor =#tag1)) AS key_1_total_matches,
SUM(DISTINCT( wtagrels.id_tag =#tag2 OR wtagrels.id_tutor =#tag2)) AS key_2_total_matches
FROM all_tag_relations AS wtagrels
WHERE ( wtagrels.id_tag =#tag1 OR wtagrels.id_tag =#tag2 )
GROUP BY wtagrels.id_wc
HAVING key_1_total_matches = 1 AND key_2_total_matches = 1
LIMIT 0, 20
It returns id_wc = 1.
For (6, 3) the query returns nothing.
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.
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
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