SQL multiple selecting from two tables and group - mysql

i have problem with select.
I managed to write a query that was doing something similar but I was able to pull data after only one I need to group as a team but with two names
I have a problem to write one question, I need to pick something about this style
team_id | player1 | player2 | position | category
1 | John Newman | Andy Roddick | 1 | 1
2 | Roger Federer | Rafael Nadal | 1 | 1
I have two tables/ one contains players and second contains attendance in team/
CREATE TABLE IF NOT EXISTS `atendance` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`player1` int(11) NOT NULL,
`player2` int(11) NOT NULL,
`position` int(11) NOT NULL,
`category` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci AUTO_INCREMENT=22 ;
CREATE TABLE IF NOT EXISTS `players` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(12) COLLATE utf8_czech_ci NOT NULL,
`surname` varchar(24) COLLATE utf8_czech_ci NOT NULL,
`datum_n` date NOT NULL,
`klub` varchar(60) COLLATE utf8_czech_ci NOT NULL,
`tel` varchar(12) COLLATE utf8_czech_ci NOT NULL,
`mail` varchar(32) COLLATE utf8_czech_ci NOT NULL,
`cas` text COLLATE utf8_czech_ci NOT NULL,
`foto` text COLLATE utf8_czech_ci NOT NULL,
`pass` text COLLATE utf8_czech_ci NOT NULL,
`is_active` tinyint(1) NOT NULL DEFAULT '0',
`valid_from` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`valid_until` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`is_admin` tinyint(1) NOT NULL DEFAULT '0',
`pohlavie` varchar(6) COLLATE utf8_czech_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci AUTO_INCREMENT=25 ;

I think you need to use JOIN particularly LEFT JOIN, like:
SELECT t.id, p1.name, p2.name, t.position, t.category
FROM atendance t
LEFT JOIN players p1
ON t.player1 = p1.id
LEFT JOIN players p2
ON t.player2 = p2.id

As much as i understood, all the required data is in first table. You just need to fire,
SELECT ID, PLAYER1, PLAYER2, POSITION, CATEGORY FROM ATENDANCE
Please note, instead of CREATE commands provide the output result of that table, as you want SELECT statement to be built. It makes easier to understand.

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.

Filtering results in MySQL in join result

I am beginner programmer. I have small problem with my MySQL Queries.
This is my MySQL data: https://pastebin.com/69PcBSVH
I have this tables:
CREATE TABLE `dishes` (
`id` bigint(20) UNSIGNED NOT NULL,
`company_id` bigint(20) UNSIGNED NOT NULL,
`name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`description` longtext COLLATE utf8mb4_unicode_ci,
`enable` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '1',
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `dish_values` (
`id` bigint(20) UNSIGNED NOT NULL,
`dishes_id` bigint(20) UNSIGNED NOT NULL,
`food_ingredient_id` bigint(20) UNSIGNED NOT NULL,
`company_id` bigint(20) UNSIGNED NOT NULL,
`quantity` decimal(9,2) NOT NULL DEFAULT '0.00'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `food_ingredients` (
`id` bigint(20) UNSIGNED NOT NULL,
`company_id` bigint(20) UNSIGNED NOT NULL,
`name` varchar(120) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`garbage` decimal(9,2) NOT NULL DEFAULT '0.00',
`energy_value` decimal(9,2) NOT NULL DEFAULT '0.00',
`protein` decimal(9,2) NOT NULL DEFAULT '0.00',
`fat` decimal(9,2) NOT NULL DEFAULT '0.00',
`available_carbohydrates` decimal(9,2) NOT NULL DEFAULT '0.00',
`roughage` decimal(9,2) NOT NULL DEFAULT '0.00',
`description` longtext COLLATE utf8mb4_unicode_ci,
`url_address` varchar(160) COLLATE utf8mb4_unicode_ci NOT NULL,
`allergen` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0',
`allergen1` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0',
`allergen2` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0',
`allergen3` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0',
`available_in_demo` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0',
`enable` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '1'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
My base is a base of dishes and ingredients needed to prepare a dish.
I need to display all dishes that are not allergens (food_ingredients.allergen = 0).
Table dishes = given dishes
Table dish_values = ingredients that are assigned to a given dish
Food_ingredients table = ingredients of the dish.
Dishes that are allergens have a value of 1 in the allergen column and those that are not allergens 0.
I make query to show my dish (without allergens):
SELECT
dishes.id, dishes.company_id, dishes.name, dishes.description, dishes.enable
FROM `dishes` join dish_values on dish_values.dishes_id = dishes.id
join food_ingredients on food_ingredients.id = dish_values.food_ingredient_id
WHERE food_ingredients.allergen = 0
GROUP BY dishes.id
But it's not working correct.
As a result, I have both dishes that contain allergens and those that do not have them.
How can I repair it?
I would use not’exists with a correlated subquery: this avoids the need for aggregation, which is generally not good for performance on large datasets:
select d.*
from dishes d
where not exists (
select 1
from dish_values v
inner join food_ingredients i on i.id = v.food_ingredient_id
where
v.dishes_id = d.id
and i.allergen = 1
)
For performance, you want the following index: food_ingredients(allergen, id).
Side note: you should change the data type of allergen from char(1) to tinyint(1) since it only contains 0/1 values.
As allergen is a char column, you need quotes around the 0. Alternatively, you can change the column to an int.
Remove the WHERE clause and add the condition to the HAVING clause:
SELECT d.id, d.company_id, d.name, d.description, d.enable
FROM dishes d
INNER JOIN dish_values v ON v.dishes_id = d.id
INNER JOIN food_ingredients i ON i.id = v.food_ingredient_id
GROUP BY d.id, d.company_id, d.name, d.description, d.enable
HAVING SUM(i.allergen) = 0
The condition SUM(i.allergen) = 0 makes sure that all matching rows from the table food_ingredients have i.allergen = 0.

MySQL QUERY for photo ranking

I have 2 tables:
CREATE TABLE `psPhotosRating` (
`id_photo_rating` int(11) NOT NULL,
`id_user` int(11) NOT NULL,
`id_uploaded_files` int(11) NOT NULL,
`rating` int(2) NOT NULL,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `psUploadedFiles2` (
`id_uploaded_files` int(10) UNSIGNED NOT NULL,
`enable` char(1) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
`id_user` int(11) NOT NULL DEFAULT '0',
`file_path` varchar(150) COLLATE utf8_unicode_ci NOT NULL,
`file_name` varchar(75) COLLATE utf8_unicode_ci NOT NULL,
`creation_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`category` bigint(20) NOT NULL DEFAULT '0',
`tags` text COLLATE utf8_unicode_ci,
`description` mediumtext COLLATE utf8_unicode_ci,
`promo_in_front` char(1) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
`count` bigint(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
ALTER TABLE `psPhotosRating`
ADD PRIMARY KEY (`id_photo_rating`);
ALTER TABLE `psPhotosRating`
MODIFY `id_photo_rating` int(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE `psUploadedFiles2`
MODIFY `id_uploaded_files` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;
COMMIT;
psUploadedFiles2 - this is the "photo database"
psPhotosRating - table with votes cast for each photo from psUploadedFiles2
Not every picture has votes.
I need a SQL query displaying a list of images (psUploadedFiles2) sorted by the psPhotosRating rank (number of votes cast).
Does anyone know how to do it?
I think you can just join here second table and count results:
SELECT count(rat.id_uploaded_files ) as rating, ps.*
FROM psUploadedFiles2 ps
JOIN psPhotosRating rat ON ps.id_uploaded_files = rat.id_uploaded_files
ORDER BY rating DESC;

MySQL query optimization improvement

Hello all I have a MySQL query that fetches data from more than on tables. Here is my query
SELECT
user_id as id,
user_name as name,
user_phone as phone,
user_email as email,
user_address1 as address1,
user_address2 as address2,
user_city as city,
user_state as state,
user_country as country,
user_available as available,
user_location as location,
user_latitude as latitude,
user_longitude as longitude,
user_description as description,
user_company as company,
user_gender as gender,
(SELECT MIN(service_price) FROM service WHERE service.user_id = a.user_id) as price,
(SELECT service_recomanded FROM service WHERE service.user_id = a.user_id ORDER BY service.service_price ASC LIMIT 1) as recomandad,
verified_email,
verified_facebook,
verified_phone,
verified_twitter,
(SELECT providerphoto_name FROM providerphoto WHERE providerphoto.user_id = a.user_id ORDER BY providerphoto_order ASC LIMIT 1 ) as photo,
(SELECT ROUND( AVG(review_rate),2) FROM review WHERE review.user_id = a.user_id ) AS rate,
(SELECT service_ICOC FROM service WHERE service.user_id = a.user_id ORDER BY service_price ASC LIMIT 1) as type
FROM
user a
WHERE a.user_type = 'provider'
AND a.user_active=1
AND a.user_deleted=0
It gets data from user table, service table, review table and providerphoto table. It works too but the execution time is very slow. I guess to make it a single query and avoid the inner five queries may run it fast. Any help?
Table structures.
--
-- Table structure for table `providerphoto`
--
CREATE TABLE IF NOT EXISTS `providerphoto` (
`providerphoto_id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`providerphoto_file` varchar(256) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`providerphoto_name` varchar(256) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`providerphoto_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`providerphoto_order` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`providerphoto_id`),
KEY `user_id` (`user_id`),
KEY `providerphoto` (`user_id`,`providerphoto_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=487 ;
-- --------------------------------------------------------
--
-- Table structure for table `review`
--
CREATE TABLE IF NOT EXISTS `review` (
`review_id` int(11) NOT NULL AUTO_INCREMENT,
`review_title` varchar(256) COLLATE utf8_unicode_ci NOT NULL,
`user_id` int(11) NOT NULL,
`review_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`review_content` text COLLATE utf8_unicode_ci NOT NULL,
`review_user_id` int(11) NOT NULL,
`review_rate` int(10) NOT NULL DEFAULT '1',
`review_tip` int(11) NOT NULL DEFAULT '0',
`service_booked` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`review_id`),
KEY `user_id` (`user_id`),
KEY `review_date` (`review_date`),
KEY `review_user_id` (`review_user_id`),
KEY `review_rate` (`review_rate`),
KEY `review_tip` (`review_tip`),
KEY `service_booked` (`service_booked`),
KEY `review` (`user_id`,`review_rate`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=97 ;
-- --------------------------------------------------------
--
-- Table structure for table `service`
--
CREATE TABLE IF NOT EXISTS `service` (
`service_id` int(11) NOT NULL AUTO_INCREMENT,
`service_name` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
`service_created_by` int(11) NOT NULL DEFAULT '0',
`service_ICOC` varchar(256) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`service_price` int(11) NOT NULL,
`service_date_added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`service_date_expire` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`service_time` varchar(256) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
`service_rate` varchar(256) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
`service_type` int(10) NOT NULL DEFAULT '1' COMMENT '1-in call, 2-out call, 3-in&out call',
`service_recomanded` int(2) NOT NULL DEFAULT '0',
`service_genre` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`service_message` text COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`service_id`),
KEY `user_id` (`user_id`),
KEY `service_ICOC` (`service_ICOC`(255)),
KEY `service` (`user_id`,`service_price`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=854 ;
-- --------------------------------------------------------
--
-- Table structure for table `user`
--
CREATE TABLE IF NOT EXISTS `user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`user_name` varchar(256) COLLATE utf8_unicode_ci DEFAULT NULL,
`user_password` varchar(256) COLLATE utf8_unicode_ci NOT NULL,
`user_email` varchar(256) COLLATE utf8_unicode_ci DEFAULT NULL,
`user_phone` varchar(256) COLLATE utf8_unicode_ci DEFAULT NULL,
`user_address1` text COLLATE utf8_unicode_ci,
`user_address2` text COLLATE utf8_unicode_ci NOT NULL,
`user_city` varchar(256) COLLATE utf8_unicode_ci NOT NULL,
`user_state` varchar(256) COLLATE utf8_unicode_ci NOT NULL,
`user_country` varchar(256) COLLATE utf8_unicode_ci NOT NULL,
`user_company` varchar(256) COLLATE utf8_unicode_ci NOT NULL,
`user_birthday` date DEFAULT NULL,
`user_register_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`user_type` enum('provider','client') COLLATE utf8_unicode_ci DEFAULT NULL,
`user_description` text COLLATE utf8_unicode_ci NOT NULL,
`user_available` int(10) NOT NULL DEFAULT '1',
`verified_email` tinyint(1) NOT NULL DEFAULT '0',
`verified_facebook` tinyint(1) NOT NULL DEFAULT '0',
`verified_phone` tinyint(1) NOT NULL DEFAULT '0',
`verified_twitter` tinyint(1) NOT NULL DEFAULT '0',
`user_facebook_friends` int(11) NOT NULL DEFAULT '0',
`user_twitter_friends` int(11) NOT NULL DEFAULT '0',
`user_longitude` decimal(10,5) NOT NULL DEFAULT '0.00000',
`user_latitude` decimal(10,5) NOT NULL DEFAULT '0.00000',
`user_deleted` tinyint(4) NOT NULL DEFAULT '0',
`user_gender` int(11) NOT NULL DEFAULT '0',
`user_facebook_token` varchar(200) COLLATE utf8_unicode_ci NOT NULL,
`user_active` tinyint(4) NOT NULL DEFAULT '1',
`user_location` varchar(200) COLLATE utf8_unicode_ci NOT NULL,
`user_push_notification_token` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`user_timezone_diff` int(11) NOT NULL DEFAULT '0',
`balanced_uri` text COLLATE utf8_unicode_ci NOT NULL,
`user_reset_passwd_token` varchar(200) COLLATE utf8_unicode_ci NOT NULL,
`is_test_user` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`user_id`),
KEY `deleted_idx` (`user_deleted`),
KEY `email_idx` (`user_email`(255))
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=426 ;
You can probably just add indexes to speed up the query. Try adding the following indexes:
service(user_id, service_price)
providerphoto(user_id, providerphoto_order)
review(user_id, review_rate)
Something like this. This will return all the values for the users as I did not use the ORDER and LIMIT in the query. This is just for the approach.
SELECT
a.user_id as id,
user_name as name,
user_phone as phone,
user_email as email,
user_address1 as address1,
user_address2 as address2,
user_city as city,
user_state as state,
user_country as country,
user_available as available,
user_location as location,
user_latitude as latitude,
user_longitude as longitude,
user_description as description,
user_company as company,
user_gender as gender,
MIN(s.service_price) as price,
s.service_recomanded as recomandad,
verified_email,
verified_facebook,
verified_phone,
verified_twitter,
pp.providerphoto_name as photo,
ROUND( AVG(r.review_rate),2) as rate,
s.service_ICOC as type
FROM
user a LEFT JOIN service s on s.user_id = a.user_id LEFT JOIN providerphoto pp on pp.user_id = a.user_id LEFT JOIN review r on r.user_id = a.user_id
WHERE a.user_type = 'provider'
AND a.user_active=1
AND a.user_deleted=0;
First thing I would do is index user.user_type, user.user_deleted and user.user_active to let the optimizer quickly start with a smaller set of user records to have to deal with.
Do you mean to allow service.user_id to be NULL? That seems like a mistake.
Also you may want to throw indexes on any columns where you're doing an ORDER BY.
The best way to find out what is slow in this query is to do a EXPLAIN QUERY and analyze what it tells you. We can help you with that as well.

How to optimize a MySQL query with EXPLAIN showing 'using temporary; using filesort'

I have a rather slow query that I'd like to optimize. EXPLAIN shows 'using temporary; using filesort'. I tried a few solutions and, doing without an ORDER BY, even managed to get rid of the 'using filesort'. But is there a way to avoid 'using temporary; using filesort' entirely, without sacrificing the ORDER BY?
This is my query:
SELECT `tags`.`name`,
`tags`.`tag_id`,
COUNT(*) AS `qty_products`
FROM `products_subsubcategories`
JOIN `products_tags` ON `products_subsubcategories`.`product_id` = `products_tags`.`product_id`
JOIN `products` ON `products_subsubcategories`.`product_id` = `products`.`product_id`
JOIN `tags` ON `products_tags`.`tag_id` = `tags`.`tag_id`
WHERE `products_subsubcategories`.`subsubcategory_id` = 55
AND `tags`.`type` = 'brand'
AND `products`.`dont_display` = 0
GROUP BY `tags`.`tag_id`
ORDER BY `tags`.`order`,
`tags`.`name`;
The subsubcategory 55 is dynamic user input.
This is the EXPLAIN result:
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE products_subsubcategories ref PRIMARY,subsubcategory_id subsubcategory_id 4 const 3982 100.00 Using temporary; Using filesort
1 SIMPLE tags ALL PRIMARY,type NULL NULL NULL 679 78.94 Using where; Using join buffer
1 SIMPLE products eq_ref PRIMARY,dont_display PRIMARY 4 mbb.products_subsubcategories.product_id 1 100.00 Using where
1 SIMPLE products_tags eq_ref PRIMARY,tag_id PRIMARY 8 mbb.products.product_id,mbb.tags.tag_id 1 100.00 Using where; Using index
(When I replace ORDER BY ... by ORDER BY NULL, the 'using filesort' disapperars. I could sort the result with PHP afterwards, although it's more convenient with MySQL, of course ...)
My tables look like this:
CREATE TABLE IF NOT EXISTS `products_subsubcategories` (
`position` smallint(5) unsigned NOT NULL DEFAULT '0',
`product_id` int(10) unsigned NOT NULL,
`subsubcategory_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`product_id`,`subsubcategory_id`),
KEY `subsubcategory_id` (`subsubcategory_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE IF NOT EXISTS `products_tags` (
`product_id` int(10) unsigned NOT NULL,
`tag_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`product_id`,`tag_id`),
KEY `tag_id` (`tag_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE IF NOT EXISTS `products` (
`article_number` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`date` date DEFAULT NULL,
`delivery_time` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`description` text COLLATE utf8_unicode_ci NOT NULL,
`dont_display` tinyint(1) unsigned NOT NULL DEFAULT '0',
`ean` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`image_error` tinyint(1) NOT NULL DEFAULT '0',
`image_is_downloaded` tinyint(1) NOT NULL DEFAULT '0',
`image_url` varchar(400) COLLATE utf8_unicode_ci NOT NULL,
`image_url_170_134` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`image_url_original_size` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
`is_duplicate` tinyint(1) unsigned NOT NULL DEFAULT '0',
`is_not_associated_to_category` tinyint(1) unsigned NOT NULL DEFAULT '0',
`is_not_associated_to_subcategory` tinyint(1) unsigned NOT NULL DEFAULT '0',
`is_not_associated_to_subsubcategory` tinyint(1) unsigned NOT NULL DEFAULT '0',
`last_association` datetime DEFAULT NULL,
`last_completion_by_ean` datetime DEFAULT NULL,
`matching_age` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`matching_brand` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`matching_category` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`matching_color` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`matching_gender` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`matching_keywords` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`matching_main_category` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`matching_size` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`matching_subcategory` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`matching_subsubcategory` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`old_price` decimal(7,2) unsigned NOT NULL DEFAULT '0.00',
`price` decimal(7,2) unsigned NOT NULL DEFAULT '0.00',
`product_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`product_list_id` int(10) unsigned NOT NULL DEFAULT '0',
`qty_overall_clicks` int(10) unsigned NOT NULL DEFAULT '0',
`shipping` decimal(7,2) unsigned NOT NULL DEFAULT '0.00',
`shop_url` varchar(400) COLLATE utf8_unicode_ci NOT NULL,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`vendor_id` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`product_id`),
KEY `article_number` (`article_number`),
KEY `dont_display` (`dont_display`),
KEY `ean` (`ean`),
KEY `is_deleted` (`is_deleted`),
KEY `is_duplicate` (`is_duplicate`),
KEY `is_not_associated_to_category` (`is_not_associated_to_category`),
KEY `is_not_associated_to_subcategory` (`is_not_associated_to_subcategory`),
KEY `is_not_associated_to_subsubcategory` (`is_not_associated_to_subsubcategory`),
KEY `product_list_id` (`product_list_id`),
KEY `vendor_id` (`vendor_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1084370;
CREATE TABLE IF NOT EXISTS `tags` (
`display_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`image_url` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`order` int(10) unsigned NOT NULL DEFAULT '0',
`tag_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`type` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`tag_id`),
KEY `type` (`type`),
KEY `name` (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1084;
I would suggest attempting a query that doesn't use JOINs, simply for the fact that you don't use the JOINs for anything other than getting a count.
Try the following:
SELECT
t.name,
t.tag_id,
(
SELECT COUNT(*) FROM product_tags pt
INNER JOIN product_subcategories ps
ON ps.product_id = pt.product_id
INNER JOIN product p
ON p.product_id = pt.product_id
WHERE pt.tag_id = t.tag_id
AND p.dont_display = 0
AND ps.subsubcategory_id = 55
) AS qty_products
FROM tags t
WHERE
t.type = 'brand'
AND EXISTS (
SELECT * FROM product_tags pt
INNER JOIN product_subcategories ps
ON ps.product_id = pt.product_id
INNER JOIN product p
ON p.product_id = pt.product_id
WHERE pt.tag_id = t.tag_id
AND p.dont_display = 0
AND ps.subsubcategory_id = 55
)
ORDER BY t.order,t.name
In this way, you only query the tags table, getting the results back in order initially. Then for each record you check if any are in subsubcategory 55, and otherwise skip that tag.
This should improve your query greatly, unless there are an enormous number of tags (and even then it still might improve things.)
Another improvement you can make is the one Kickstart suggested in the comments: add a covering index to the tags table:
ALTER TABLE tags
ADD INDEX `type_order_name` (`type`,`order`,`name`)
If you aren't familiar with multipart keys, just know that internally they are effectively stored as a concatenation of each of the columns, in the order the columns are listed in the key definition.
Therefore as long as you provide the type in the WHERE clause, the tags will be stored ordered by order and name, just as this query wants them. This will result in very fast sorting (because they will already be sorted in the index).