mysql query using the wrong indexes - mysql

I have some optimization problems with some of my queries in a mysql database. After I build my application I am trying to optimize using mysqltuner and explain, to find non indexed queries. This is a query that is running often and reports that is not using the index :
SELECT count(*) AS rangedandselling
FROM
( SELECT DISTINCT `store_formats`.`Store Name`
FROM (`eds_sales`
JOIN `store_formats`
ON (`eds_sales`.`Store Nbr` = `store_formats`.`Store Nbr`)
)
WHERE `eds_sales`.`Prime Item Nbr` = '4'
AND `eds_sales`.`Date` BETWEEN CAST('2016-07-14' AS DATETIME)
AND CAST('2016-07-21' AS DATETIME)
AND `store_formats`.`Format Name` IN ('format1','format2')
AND `store_formats`.`Store Name` IN (
SELECT DISTINCT `store_formats`.`Store Name`
FROM (`eds_stock`
JOIN `store_formats`
ON (`eds_stock`.`Store Nbr` = `store_formats`.`Store Nbr`)
)
WHERE `eds_stock`.`Prime Item Nbr` = '4'
AND `eds_stock`.`Date` BETWEEN CAST('2016-07-14' AS DATETIME)
AND CAST('2016-07-21' AS DATETIME)
AND `store_formats`.`Format Name` IN ('format1','format2')
AND `eds_stock`.`Curr Traited Store/Item Comb.` = '1' )
) t
This is the explain output : https://tools.mariadb.org/ea/pyb3h
Although I have indexed the columns involved in the joins and lookups, it looks like it is picking another index. this other index is called uniqness, and is composed of 6 different columns in the source columns that I use for inserts (the combination of those columns is the only thing that makes a row unique, hence the name I gave.). I then made sure I have indexes for the other columns and I can see them in the explain. I am not sure why this happens, can someone help?
Any ideas on optimizing this query?
Here is the explain for those that the link above does not work :
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+---+---+---+---+---+---+---+---+---+---+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 167048 | |
| 2 | DERIVED | eds_sales | ref | uniqness,Prime Item Nbr,Store Nbr | uniqness | 4 | const | 23864 | Using where; Using index; Using temporary |
| 2 | DERIVED | store_formats | ref | Store Nbr,Store Name,Format Name | Store Nbr | 5 | equidata.eds_sales.Store Nbr | 1 | Using where |
| 2 | DERIVED | <subquery3> | eq_ref | distinct_key | distinct_key | 84 | func | 1 | Distinct |
| 3 | MATERIALIZED | store_formats | ALL | Store Nbr,Store Name,Format Name | NULL | NULL | NULL | 634 | Using where; Distinct |
| 3 | MATERIALIZED | eds_stock | ref | uniqness,Prime Item Nbr,Store Nbr | uniqness | 8 | const,equidata.store_formats.Store Nbr | 7 | Using where; Distinct |
+---+---+---+---+---+---+---+---+---+---+
I am also posting the related tables structure :
--
-- Table structure for table `eds_sales`
--
CREATE TABLE `eds_sales` (
`id` int(12) NOT NULL,
`Prime Item Nbr` int(12) NOT NULL,
`Prime Item Desc` varchar(255) NOT NULL,
`Prime Size Desc` varchar(255) NOT NULL,
`Variety` varchar(255) NOT NULL,
`WHPK Qty` int(5) NOT NULL,
`SUPPK Qty` int(5) NOT NULL,
`Depot Nbr` int(5) NOT NULL,
`Depot Name` varchar(255) NOT NULL,
`Store Nbr` int(5) NOT NULL,
`Store Name` varchar(255) NOT NULL,
`EPOS Quantity` int(5) NOT NULL,
`EPOS Sales` float(4,2) NOT NULL,
`Date` date NOT NULL,
`Client` varchar(255) NOT NULL,
`Retailer` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `eds_sales`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `uniqness` (`Prime Item Nbr`,`Prime Item Desc`,`Prime Size Desc`,`Variety`,`WHPK Qty`,`SUPPK Qty`,`Depot Nbr`,`Depot Name`,`Store Nbr`,`Store Name`,`Date`,`Client`) USING BTREE,
ADD KEY `Prime Item Nbr` (`Prime Item Nbr`),
ADD KEY `Store Nbr` (`Store Nbr`);
Table structure for table eds_stock
CREATE TABLE `eds_stock` (
`Prime Item Nbr` int(12) NOT NULL,
`Prime Item Desc` varchar(255) NOT NULL,
`Prime Size Desc` varchar(255) NOT NULL,
`Variety` varchar(255) NOT NULL,
`Curr Valid Store/Item Comb.` int(12) NOT NULL,
`Curr Traited Store/Item Comb.` int(12) NOT NULL,
`Store Nbr` int(12) NOT NULL,
`Store Name` varchar(255) NOT NULL,
`Curr Str On Hand Qty` int(12) NOT NULL,
`Curr Str In Transit Qty` int(12) NOT NULL,
`Curr Str On Order Qty` int(12) NOT NULL,
`Curr Str In Depot Qty` int(12) NOT NULL,
`Curr Instock %` int(12) NOT NULL,
`Max Shelf Qty` int(12) NOT NULL,
`On Hand Qty` int(12) NOT NULL,
`Date` date NOT NULL,
`Client` varchar(255) NOT NULL,
`Retailer` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `eds_stock`
ADD UNIQUE KEY `uniqness` (`Prime Item Nbr`,`Store Nbr`,`Date`,`Client`,`Retailer`),
ADD KEY `Prime Item Nbr` (`Prime Item Nbr`),
ADD KEY `Store Nbr` (`Store Nbr`),
ADD KEY `Curr Valid Store/Item Comb.` (`Curr Valid Store/Item Comb.`);
Table structure for table store_formats
CREATE TABLE `store_formats` (
`id` int(12) NOT NULL,
`Store Nbr` int(4) DEFAULT NULL,
`Store Name` varchar(27) DEFAULT NULL,
`City` varchar(19) DEFAULT NULL,
`Post Code` varchar(9) DEFAULT NULL,
`Region #` int(2) DEFAULT NULL,
`Region Name` varchar(10) DEFAULT NULL,
`Distr #` int(3) DEFAULT NULL,
`Dist Name` varchar(26) DEFAULT NULL,
`Square Footage` varchar(7) DEFAULT NULL,
`Format` int(1) DEFAULT NULL,
`Format Name` varchar(23) DEFAULT NULL,
`Store Type` varchar(20) DEFAULT NULL,
`TV Region` varchar(12) DEFAULT NULL,
`Pharmacy` varchar(3) DEFAULT NULL,
`Optician` varchar(3) DEFAULT NULL,
`Home Shopping` varchar(3) DEFAULT NULL,
`Retailer` varchar(15) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `store_formats`
ADD PRIMARY KEY (`id`),
ADD KEY `Store Nbr` (`Store Nbr`),
ADD KEY `Store Name` (`Store Name`),
ADD KEY `Format Name` (`Format Name`);

CAST('2016-07-14' AS DATETIME) -- the CAST is not needed; '2016-07-14' works fine. (Especially since you are comparing against a DATE.)
IN ( SELECT ... ) is inefficient. Change to a JOIN.
On eds_stock, instead of
INDEX(`Prime Item Nbr`)
have these two:
INDEX(`Prime Item Nbr`, `Date`)
INDEX(`Prime Item Nbr`, `Curr Traited Store/Item Comb.`, `Date`)
INT is always a 4-byte number, even if you say int(2). Consider switching to TINYINT UNSIGNED (and other sizes of INT).
float(4,2) -- Do not use (m,n); it causes an extra rounding and my cause undesired truncation. Either use DECIMAL(4,2) (for money), or plain FLOAT.
Bug?? Did you really want 8 days, not just a week in
AND `Date` BETWEEN CAST('2016-07-14' AS DATETIME) AND CAST('2016-07-21' AS DATETIME)
I like this pattern:
AND `Date` >= '2016-07-14'
AND `Date` < '2016-07-14' + INTERVAL 1 WEEK
Instead of two selects
SELECT count(*) AS rangedandselling
FROM ( SELECT DISTINCT `store_formats`.`Store Name` ...
One select will probably work (and be faster):
SELECT COUNT(DISTINCT `store_formats`.`Store Name`) AS rangedandselling ...
Once you have cleaned up most of that, we can get back to your question about 'wrong index', if there is still an issue. (Please start a new Question if you need further help.)

Related

Order by slow on joined large table

I have 3 large table around 1 table have 1,000,000 rows other 2 have 500,000 rows.
When I use ORDER BY in query it takes 15 seconds (without ORDER BY 0.001s).
I already try add index on invoice.created_at and invoice.invoice_id
But still take a lot of time.
SELECT
`i`.*,
GROUP_CONCAT(DISTINCT ii.item_id SEPARATOR ',') AS item_id,
GROUP_CONCAT(DISTINCT it.transaction_id SEPARATOR ',') AS transactions,
SUM(DISTINCT IF(ii.status = 1, ii.subtotal, 0)) AS subtotal,
SUM(DISTINCT it.subtotal) AS total_paid_amount,
SUM(DISTINCT it.additional_fee) AS total_additional_fee_amount,
SUM(ii.quantity) AS total_quantity,
(total_amount - SUM(DISTINCT COALESCE(it.amount, 0))) AS total_balance
FROM
`invoices` `i`
LEFT JOIN
`invoices_items` `ii` ON `ii`.`invoice_id` = `i`.`invoice_id`
LEFT JOIN
`invoices_transactions` `it` ON `it`.`invoice_id` = `i`.`invoice_id`
AND `it`.`process_status` = 1
AND `it`.`status` = 1
WHERE
`i`.`status` = '1'
GROUP BY `i`.`invoice_id`
ORDER BY `i`.`created_at` DESC
LIMIT 50
Query with EXPLAIN:
+----+-------------+-------+------------+-------+--------------------------------------------------+------------+---------+------------------------------+--------+----------+----------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+--------------------------------------------------+------------+---------+------------------------------+--------+----------+----------------------------------------------+
| 1 | SIMPLE | i | NULL | index | PRIMARY,customer_id,code,invoice_id_2,created_at | PRIMARY | 4 | NULL | 473309 | 10.00 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | ii | NULL | ref | invoice_id | invoice_id | 5 | test.i.invoice_id | 2 | 100.00 | NULL |
| 1 | SIMPLE | it | NULL | ref | invoice_id,status | invoice_id | 5 | test.i.invoice_id | 1 | 100.00 | Using where |
+----+-------------+-------+------------+-------+--------------------------------------------------+------------+---------+------------------------------+--------+----------+----------------------------------------------+
I try to remove SUM ,GROUP_CONCAT and WHERE CASE,
But still need 10 sec
SELECT
`i`.*
FROM
`invoices` `i`
LEFT JOIN
`invoices_items` `ii` ON `ii`.`invoice_id` = `i`.`invoice_id`
LEFT JOIN
`invoices_transactions` `it` ON `it`.`invoice_id` = `i`.`invoice_id`
GROUP BY `i`.`invoice_id`
ORDER BY `i`.`created_at` DESC
LIMIT 50
Create Table:
CREATE TABLE `invoices` (
`invoice_id` int NOT NULL AUTO_INCREMENT,
`warehouse_id` tinyint DEFAULT NULL,
`invoice_type` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i NOT NULL DEFAULT 'C',
`invoice_date` date DEFAULT NULL,
`customer_id` int DEFAULT NULL,
`contact_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`contact_no` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`contact_email` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`address` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`total_amount` deimal(10,2) DEFAULT '0.00',
`total_cost` deimal(20,2) DEFAULT NULL,
`delivery_type` tinyint DEFAULT '0',
`remark` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i,
`payment_status` tinyint(1) NOT NULL DEFAULT '3',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT NULL,
`updated_by` int NOT NULL,
`confirmed_at` datetime DEFAULT NULL,
`status` tinyint(1) NOT NULL DEFAULT '2'
PRIMARY KEY (`invoice_id`),
KEY `customer_id` (`customer_id`),
KEY `code` (`code`),
KEY `invoice_id_2` (`invoice_id`,`created_at`),
KEY `created_at` (`created_at`)
) ENGINE=InnoDB AUTO_INCREMENT=513697 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_i;
CREATE TABLE `invoices_items` (
`item_id` int NOT NULL AUTO_INCREMENT,
`invoice_id` int NOT NULL,
`warehouse_id` int DEFAULT NULL,
`product_id` int DEFAULT NULL,
`variant_id` int DEFAULT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`quantity` int DEFAULT '1',
`unit_price` deimal(10,2) DEFAULT NULL,
`cost` deimal(10,2) DEFAULT NULL,
`subtotal` deimal(10,2) DEFAULT NULL,
`serial_no` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`StockoutDate` datetime DEFAULT NULL,
`ReturnDate` datetime DEFAULT NULL,
`Return_StockLocationID` tinyint DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT NULL,
`status` tinyint(1) NOT NULL DEFAULT '1',
`priority` int NOT NULL DEFAULT '0',
PRIMARY KEY (`item_id`),
KEY `invoice_id` (`invoice_id`),
KEY `variant_id` (`variant_id`),
KEY `quantity` (`quantity`),
KEY `unit_price` (`unit_price`),
KEY `subtotal` (`subtotal`),
KEY `created_at` (`created_at`),
KEY `updated_at` (`updated_at`),
KEY `status` (`status`),
KEY `priority` (`priority`),
KEY `variant_id_2` (`variant_id`,`quantity`),
KEY `variant_id_3` (`variant_id`,`name`,`quantity`),
KEY `product_id` (`product_id`),
KEY `StockoutDate` (`StockoutDate`)
) ENGINE=InnoDB AUTO_INCREMENT=1200951 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_i;
CREATE TABLE `invoices_transactions` (
`transaction_id` int NOT NULL AUTO_INCREMENT,
`invoice_id` int DEFAULT NULL,
`warehouse_id` int DEFAULT NULL,
`invoice_date` date DEFAULT NULL,
`payment_id` int DEFAULT NULL,
`balance` deimal(10,2) DEFAULT NULL,
`amount` deimal(10,2) DEFAULT NULL,
`additional_rate` deimal(10,2) DEFAULT NULL,
`additional_fee` deimal(10,2) DEFAULT NULL,
`subtotal` deimal(10,2) DEFAULT NULL,
`process_status` int DEFAULT '1',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`payment_status` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT NULL,
`status` tinyint NOT NULL DEFAULT '1',
`CreatedByID` int DEFAULT NULL,
`ModifiedByID` int DEFAULT NULL,
`priority` int DEFAULT '0',
PRIMARY KEY (`transaction_id`),
KEY `invoice_id` (`invoice_id`),
KEY `payment_status` (`payment_status`),
KEY `created_at` (`created_at`),
KEY `updated_at` (`updated_at`),
KEY `status` (`status`),
KEY `amount` (`amount`),
KEY `additional_fee` (`additional_fee`),
KEY `subtotal` (`subtotal`),
KEY `transaction_id` (`transaction_id`,`amount`,`additional_fee`,`subtotal`),
KEY `invoice_id_2` (`invoice_id`,`process_status`,`status`),
KEY `process_status` (`process_status`),
KEY `transaction_id_2` (`transaction_id`,`invoice_id`,`amount`,`additional_fee`,`subtotal`,`process_status`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=543606 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_i;
Let's find the 50 invoices first, then reach for the rest of the stuff:
SELECT ... (all the current stuff)
FROM ( SELECT invoice_id
FROM invoices
WHERE status = 1
ORDER BY created_at DESC
LIMIT 50
) AS ids
JOIN invoices AS i USING(invoice_id)
LEFT JOIN ... ON ...
LEFT JOIN ... ON ...
GROUP BY invoice_id
ORDER BY created_id DESC
These composite indexes may help:
i: (status, created_at, invoice_id)
it: (status, process_status, invoice_id)
ii: (invoice_id, quantity, subtotal, status, item_id)
Redundant index:
KEY `variant_id` (`variant_id`) -- since this is the prefix of others
Because of PRIMARY KEY(transaction_id), the keys starting with transaction_id are redundant.
Summary:
I provided a "covering index" with the columns in the right order to allow finding the 50 ids directly from that index.
The rest of the work is limited to the 50 rows (or sets of rows, since I suspect that the rest are many:1 with respect to invoices.)
Your EXPLAIN shows lots of "Rows" and a "filesort" -- indicating that it gathered everything, then did the group by, then sorted, and finally, peeled of 50.
I suspect the "filesort" was really two sorts. See EXPLAIN FORMAT=JSON ... to find out.

Not showing all the selected field using SELECT query - MySQL

I am trying to select some fields from the table with customized values, When I run the select query its showing first two selected columns irrespective of the order in select query (like country,name,slug,area_slug or country,slug,area_slug,name).
please find the table schema and the query below.
TABLE:
CREATE TABLE `destination` (
`code` varchar(5) NOT NULL,
`country` varchar(2) NOT NULL,
`region` varchar(50) NOT NULL,
`name` varchar(50) NOT NULL,
`parent` varchar(5) DEFAULT NULL,
`latitude` double(30,15) NOT NULL,
`longitude` double(30,15) NOT NULL,
`updated` varchar(30) NOT NULL DEFAULT '0000-00-00T00:00:00.000000Z'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
QUERY:
SELECT 'ae' as country,
name,
replace(lcase(name), ' ', '-') as slug,
CONCAT(replace(lcase(name), ' ', '-'),'-united-arab-emirates') as area_slug
FROM `destination`
WHERE code IN ("18ed5","18ed6","18ed7","18ed8","18ed9","18eda","18edb")
According to the error message in the screenshot provided you do not have a unique column in the query, and that is true. There does not even seem to be one in the table. I suggest you add an auto incrementing ID into your table, then include that column in your query.
for example:
DROP TABLE IF EXISTS `destination`;
CREATE TABLE `destination` (
`id` mediumint(8) unsigned NOT NULL auto_increment primary key,
`code` varchar(5) NOT NULL,
`country` varchar(2) NOT NULL,
`region` varchar(50) NOT NULL,
`name` varchar(50) NOT NULL,
`parent` varchar(5) DEFAULT NULL,
`latitude` double(30,15) NOT NULL,
`longitude` double(30,15) NOT NULL,
`updated` varchar(30) NOT NULL DEFAULT '0000-00-00T00:00:00.000000Z'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
INSERT INTO `destination` (code,country,region,name,parent,latitude,longitude,updated)
VALUES ('18ed5','1','1','1','1','1',1,2);
SELECT ID
, 'ae' AS country
, name
, REPLACE(Lcase(name), ' ', '-') AS slug
, Concat(REPLACE(Lcase(name), ' ', '-'), '-united-arab-emirates') AS area_slug
FROM `destination`
WHERE code IN ( '18ed5', '18ed6', '18ed7', '18ed8','18ed9', '18eda', '18edb' )
result
| ID | country | name | slug | area_slug |
|----|---------|------|------|------------------------|
| 1 | ae | 1 | 1 | 1-united-arab-emirates |
see: http://rextester.com/LER16583

Enhancing performance when applying multiple joins in mysql

I have 3 tables:
Nutritions_facts table which is static table that have all nutrition nutrition_id and nutrition_ename
Products_info table which has products info and the columns are product_id, product_ename, brand
product_nutrition_facts
which is mediator table that links the product with its nutrition facts with their values . The structure is product_id, nutrition_id, nutrition_value .. Each product can have 1 row or more depending on number of nutrition facts it has.
Here is a real testing example
Nutrition_facts table
nutrition_id |nutrition_ename
1 | caloreis
2 | fat
3 | sugar
4 | salt
Products_info table
product_id| product_ename | brand
1 | Nutella Hazelnut Cocoa | Nutella
2 | Nutella Jar | Nutella
product_nutrition_facts table
product_id | nutrition_id | nutrition_value
1 | 1 | 200
1 | 2 | 15
1 | 3 | 2
1 | 4 | 11
2 | 1 | 200
2 | 2 | 15
2 | 3 | 12
2 | 4 | 11
I need to make query that returns me the products' name with value of sugar is less than or equla 15 and salt less than or equal 140
I build a query that return correct values but it takes long time to process. Can someone suggest edits to enhance the performance please..
SELECT DISTINCT p.product_id, p.brand, p.e_name, p.image_low
FROM products_info p
JOIN product_nutrition_facts pn ON p.product_id = pn.product_id
WHERE p.brand = 'AL MARAI'
AND (
(
p.product_id
IN (
SELECT product_id
FROM product_nutrition_facts pn
WHERE pn.nutrition_id =3
AND pn.nutrition_value <=15
)
)
AND (
p.product_id
IN (
SELECT product_id
FROM product_nutrition_facts pn
WHERE pn.nutrition_id =4
AND pn.nutrition_value <=140
)
)
)
AND (
pn.nutrition_id =3
OR pn.nutrition_id =4
)
EDITS
CREATE TABLE `products_info` (
`product_id` int(11) NOT NULL AUTO_INCREMENT,
`image_low` varchar(400) DEFAULT NULL,
`e_name` varchar(200) DEFAULT NULL,
PRIMARY KEY (`product_id`),
UNIQUE KEY `product_id_UNIQUE` (`product_id`)
) ENGINE=InnoDB AUTO_INCREMENT=249292 DEFAULT CHARSET=utf8
CREATE TABLE `product_nutrition_facts` (
`prod_nut_id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) DEFAULT NULL,
`nutrition_id` int(11) DEFAULT NULL,
`nutrition_value` varchar(25) DEFAULT NULL,
`unit_id` int(11) DEFAULT NULL,
`parent` int(11) DEFAULT NULL,
`serving_size` varchar(145) DEFAULT NULL,
`serving_size_unit` int(11) DEFAULT NULL,
`no_nutrition_facts` int(11) NOT NULL,
`added_by` varchar(145) DEFAULT NULL,
`last_update` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`inserted_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_by` varchar(150) NOT NULL,
PRIMARY KEY (`prod_nut_id`),
UNIQUE KEY `prod_nut_id_UNIQUE` (`prod_nut_id`),
KEY `nutrition_id_fk_idx` (`nutrition_id`),
KEY `unit_Fk_idx` (`unit_id`),
KEY `unit_fk1_idx` (`serving_size_unit`),
KEY `product_idFK` (`product_id`)
) ENGINE=InnoDB AUTO_INCREMENT=580809 DEFAULT CHARSET=utf8
CREATE TABLE `nutrition_facts` (
`nutrition_id` int(11) NOT NULL AUTO_INCREMENT,
`nutrition_aname` varchar(145) DEFAULT NULL,
`nutrition_ename` varchar(145) DEFAULT NULL,
`alternative_name` varchar(145) DEFAULT NULL,
`unit` varchar(8) NOT NULL,
`daily_value` int(11) NOT NULL,
`nut_order` int(2) NOT NULL,
`is_child` int(1) NOT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`nutrition_id`)
) ENGINE=InnoDB AUTO_INCREMENT=53 DEFAULT CHARSET=utf8
Try to add indexes product_nutrition_facts (nutrition_id,nutrition_value,product_id), product_nutrition_facts (product_id,nutrition_id,nutrition_value), products_info (brand) and perfom query
SELECT p.*
FROM products_info p
join product_nutrition_facts pn1 on
pn1.product_id=p.product_id
AND pn1.nutrition_id=3
AND pn1.nutrition_value<=15
join product_nutrition_facts pn2 on
pn2.product_id=p.product_id
AND pn2.nutrition_id=4
AND pn2.nutrition_value<=140
where
p.brand = 'AL MARAI'
IN ( SELECT ... ) -- Does not optimize well; turn into JOIN (as Max suggests)
"Overnormalization" -- Nutrition_facts can be eliminated; simply use the nutrition_names in place of nutrition_id.

optimize mysql query across multiple tables

I am having some issues optimizing the following query that joins multiple tables.
SELECT count(*) as count, `snapshot_id`, `snapshot_guid`, `image`, `subject`, `name`, `brands`.`facebook`, `brands`.`brand_id`, `brand_guid`, `date_sent`
FROM (`snapshots`)
INNER JOIN `brands` ON `snapshots`.`brand_id` = `brands`.`brand_id`
WHERE `snapshots`.`status` = 1
AND `brands`.`status` = 1
AND `brands`.`archive` = 0
GROUP BY `snapshots`.`brand_id`, `snapshots`.`subject`
ORDER BY `date_sent` desc
LIMIT 20
Execution Time: 4.9 seconds
Using EXPLAIN:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE brands ALL PRIMARY,brand_id,status,brand_status NULL NULL NULL 338 Using where; Using temporary; Using filesort
1 SIMPLE snapshots ref brand_id,status,snapshot_brand_status snapshot_brand_status 5 mockd_catalog.brands.brand_id,const 166
Describe Tables for Brands and Snapshots:
Describe brands
Field Type Null Key Default Extra
brand_id int(11) NO PRI NULL auto_increment
brand_guid char(12) NO MUL NULL
friendly varchar(128) YES NULL
name varchar(128) NO NULL
url varchar(2048) YES NULL
logo text YES NULL
cover text YES NULL
facebook varchar(2048) YES NULL
address_1 varchar(128) YES NULL
address_2 varchar(128) YES NULL
city varchar(50) YES NULL
state varchar(50) YES NULL
postal varchar(20) YES NULL
country_code varchar(128) YES NULL
date_created datetime YES NULL
date_modified datetime YES NULL
hp_snapshot tinyint(1) NO 0
status tinyint(1) YES MUL 0
archive tinyint(1) NO 0
Describe snapshots
Field Type Null Key Default Extra
snapshot_id int(11) NO PRI NULL auto_increment
snapshot_guid char(36) YES MUL NULL
brand_id int(11) NO MUL 0
email varchar(256) NO MUL NULL
seed_email varchar(256) NO NULL
date_sent datetime NO MUL NULL
date_created datetime NO NULL
date_modified datetime YES NULL
content_type varchar(10) YES NULL
subject varchar(256) NO NULL
source longtext YES NULL
html longtext NO NULL
html_error text YES NULL
thumbnail text YES NULL
image text YES NULL
status tinyint(1) NO MUL 0
archive tinyint(1) NO 0
tags text YES NULL
I've tried everything that I can think of, and can't seem to further optimize this query. Any help is appreciated.
Rick
edit 3/22/2015 # 10:27am ET:
CREATE TABLE `snapshots` (
`snapshot_id` int(11) NOT NULL AUTO_INCREMENT,
`snapshot_guid` char(36) DEFAULT NULL,
`brand_id` int(11) NOT NULL DEFAULT '0',
`email` varchar(256) NOT NULL,
`seed_email` varchar(256) NOT NULL,
`date_sent` datetime NOT NULL,
`date_created` datetime NOT NULL,
`date_modified` datetime DEFAULT NULL,
`content_type` varchar(10) DEFAULT NULL,
`subject` varchar(256) NOT NULL,
`source` longtext,
`html` longtext NOT NULL,
`html_error` text,
`thumbnail` text,
`image` text,
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '-1= error, 0 = new, 1 = approved, 2 = review ',
`archive` tinyint(1) NOT NULL DEFAULT '0',
`tags` text,
PRIMARY KEY (`snapshot_id`),
KEY `snapshot_id` (`snapshot_id`) USING BTREE,
KEY `brand_id` (`brand_id`),
KEY `email` (`email`(255)),
KEY `status` (`status`),
KEY `snapshot_guid` (`snapshot_guid`) USING BTREE,
KEY `subject` (`subject`(255)),
KEY `archive` (`archive`),
KEY `archive_status` (`archive`,`status`),
KEY `date_sent` (`date_sent`) USING BTREE,
KEY `recent_snapshots` (`snapshot_id`,`snapshot_guid`,`archive`,`status`,`brand_id`,`date_sent`,`subject`(255)) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=95002 DEFAULT CHARSET=utf8;
Brands Table:
CREATE TABLE `brands` (
`brand_id` int(11) NOT NULL AUTO_INCREMENT,
`brand_guid` char(12) NOT NULL,
`friendly` varchar(128) DEFAULT NULL,
`name` varchar(128) NOT NULL,
`url` varchar(2048) DEFAULT NULL,
`logo` text,
`cover` text,
`facebook` varchar(2048) DEFAULT NULL,
`address_1` varchar(128) DEFAULT NULL,
`address_2` varchar(128) DEFAULT NULL,
`city` varchar(50) DEFAULT NULL,
`state` varchar(50) DEFAULT NULL,
`postal` varchar(20) DEFAULT NULL,
`country_code` varchar(128) DEFAULT NULL,
`date_created` datetime DEFAULT NULL,
`date_modified` datetime DEFAULT NULL,
`hp_snapshot` tinyint(1) NOT NULL DEFAULT '0',
`status` tinyint(1) DEFAULT '0',
`archive` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`brand_id`),
KEY `brand_guid` (`brand_guid`) USING BTREE,
KEY `brand_id` (`brand_id`),
KEY `status` (`status`) USING BTREE,
KEY `archive_status` (`archive`,`status`),
KEY `archive` (`archive`)
) ENGINE=InnoDB AUTO_INCREMENT=423 DEFAULT CHARSET=utf8;
The select statement:
SELECT
COUNT(*) AS count,
`snapshot_id`,
`snapshot_guid`,
`image`,
`subject`,
`name`,
`brands`.`facebook`,
`brands`.`brand_id`,
`brand_guid`,
`date_sent`
FROM
(`snapshots`)
INNER JOIN
`brands` ON `snapshots`.`brand_id` = `brands`.`brand_id`
WHERE
`snapshots`.`archive` = 0
AND `snapshots`.`status` = 1
AND `brands`.`archive` = 0
AND `brands`.`status` = 1
GROUP BY `snapshots`.`brand_id` , `snapshots`.`subject`
ORDER BY `date_sent` DESC
LIMIT 20;
Explain:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE snapshots ref brand_id,status,archive,archive_status status 1 const 48304 Using where; Using temporary; Using filesort
1 SIMPLE brands eq_ref PRIMARY,brand_id,status,archive_status,archive PRIMARY 4 mockd_catalog.snapshots.brand_id 1 Using where
Ther are several problems :
The key on date_send is defined using hash while you are using it in order by date_sent.
You have a where clause with brand.status and brand.archive. archive is only in a combined key brand_id, status, archive, but it cannot be used because the first column (brand_id) is not used in the where clause. Create an index for archive only, or a composite (status, archive).
subject needs an index on its own. Currently its index is buried deep down in a composite index.
As a general rule the order of the columns in a composite indexes important. The index is only useful if the used columns are the first ones. Also they are less effective the wider the first column is. This means that
KEY `snap_indx` (`snapshot_id`,`snapshot_guid`,`email`(255),`date_sent`,`subject`(255),`status`,`archive`)
is inefficient because subject with its 255 bytes comes before status and archive which have only 4 bytes each.

mySQL update column based on multiple rows of another table

I have two tables, dma_projects and projectsteps:
dma_projects has the following fields:
projectID
projectName
projectInstructions
CREATE TABLE IF NOT EXISTS `dma_projects` (
`projectID` int(11) NOT NULL DEFAULT '0',
`projectName` varchar(100) DEFAULT NULL,
`projectDescription` text,
`projectImage` varchar(255) DEFAULT NULL,
`projectThumb` varchar(255) DEFAULT NULL,
`projectCategory` varchar(50) DEFAULT NULL,
`projectTheme` varchar(50) DEFAULT NULL,
`projectInstructions` text,
`projectAuthorID` int(11) DEFAULT NULL,
`projectViews` int(11) DEFAULT NULL,
`projectDifficulty` varchar(20) DEFAULT NULL,
`projectTimeNeeded` varchar(40) DEFAULT NULL,
`projectDateAdded` int(11) DEFAULT NULL,
`projectStatus` varchar(20) DEFAULT NULL,
`projectVisible` varchar(1) DEFAULT NULL,
PRIMARY KEY (`projectID`),
KEY `user_id` (`projectAuthorID`),
KEY `date` (`projectDateAdded`),
FULLTEXT KEY `image` (`projectImage`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
projectsteps has:
stepID
stepno
stepdesc
project_id
CREATE TABLE IF NOT EXISTS `projectsteps` (
`projectStep_id` int(11) NOT NULL AUTO_INCREMENT,
`stepno` int(11) DEFAULT '0',
`stepdesc` text CHARACTER SET utf8 COLLATE utf8_bin,
`project_id` int(11) NOT NULL,
PRIMARY KEY (`projectStep_id`)
)
I want to update dma_projects.projectInstructions with the values of any rows that are found in the projectsteps table that have the same projectID.
I.e.
projectID 1 in dma_projects has 5 records in projectsteps, the stepdesc of those five records should be joined (separated by a ) and then updated to the projectInstructions field of the dma_projects table.
I'm scratching my head as how to write the query. Here is where I am so far but I can't get it working. The error it says is:
Unknown column 'projectsteps.stepno' in 'field list'
Here is the query:
UPDATE `dma_projects` t1
SET t1.`projectInstructions` =
(
SELECT
`projectsteps`.`stepno`,
group_concat(`projectsteps`.`stepdesc` separator '<br/>')
FROM `projectsteps`
AS somevar
INNER JOIN `projectsteps` t2
ON t1.projectID=t2.project_id
ORDER BY t2.stepno ASC
)
UPDATED
UPDATE dma_projects p JOIN
(
SELECT project_id, GROUP_CONCAT(CONCAT('<li>', stepdesc, '</li>') SEPARATOR '') instructions
FROM
(
SELECT project_id, stepdesc
FROM projectsteps
ORDER BY project_id, stepdesc
) a
GROUP BY project_id
) d ON d.project_id = p.projectid
SET p.projectInstructions = d.instructions
Sample output:
| PROJECTID | PROJECTNAME | PROJECTINSTRUCTIONS |
------------------------------------------------------------------------------------------
| 1 | project1 | <li>step11</li><li>step12</li><li>step13</li><li>step14</li> |
| 2 | project1 | <li>step21</li><li>step22</li><li>step23</li> |
Here is SQLFiddle demo