I’m projecting an e-commerce web-app. I have to give the user a set of filters by many goods properties.
In my case, a item in shop may have a many properties, and properties united in collections. For example:
Collection «Bikes» have a «Bryce type» and «Height» properties;
collection «TV» have a «Diagonal» and «Technology».
A items have a property_collection_id key reference to collections table.
The sql query should select a bike with disk braces and height more than 17'', or tv with diagonal more than 100'' and plasma technology (for example).
The table structure is
#
# Sale item
#
CREATE TABLE `object` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`property_collection_id` int(11) DEFAULT NULL,
`price` double DEFAULT NULL,
`description` text COLLATE utf8_unicode_ci,
`state_id` int(11) DEFAULT NULL COMMENT 'State of object, reference to other table',
`created_at` int(11) DEFAULT NULL,
`updated_at` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `object_to_collection` (`property_collection_id`),
KEY `object_state_fk` (`state_id`),
CONSTRAINT `object_state_fk` FOREIGN KEY (`state_id`) REFERENCES `object_state` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `object_to_collection` FOREIGN KEY (`property_collection_id`) REFERENCES `property_collection` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
#
# Collection of possible item property
#
CREATE TABLE `property_collection` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`slug_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Name without spaces and punctuation',
`description` text COLLATE utf8_unicode_ci,
`sort` int(11) NOT NULL DEFAULT '100',
`title_field` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
#
# Description of one property
#
CREATE TABLE `property` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`slug_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`type` enum('boolean','string','integer','list') COLLATE utf8_unicode_ci NOT NULL DEFAULT 'string',
`require` enum('1','0') COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
`property_collection_id` int(11) DEFAULT NULL,
`sort` int(11) NOT NULL DEFAULT '100',
`units` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `property_to_collection` (`property_collection_id`),
CONSTRAINT `property_to_collection` FOREIGN KEY (`property_collection_id`) REFERENCES `property_collection` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
#
# Item property values
#
CREATE TABLE `property_values` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`object_id` int(11) DEFAULT NULL,
`property_id` int(11) DEFAULT NULL,
`value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `value_to_object` (`object_id`),
KEY `value_to_property` (`property_id`),
CONSTRAINT `value_to_property` FOREIGN KEY (`property_id`) REFERENCES `property` (`id`),
CONSTRAINT `value_to_object` FOREIGN KEY (`object_id`) REFERENCES `real_estate_object` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=161 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Code in http://pastebin.com/zJNApskJ
The query that I create is not (and will not) work:
SELECT `object`.* FROM `object`
LEFT JOIN `property_values` ON `object`.`id` = `property_values`.`object_id`
LEFT JOIN `property` ON `property_values`.`property_id` = `property`.`id`
WHERE
(
((`property_values`.`value`='7') AND (`property_values`.`value`='1'))
AND
(CAST(`property_values`.`value` AS DECIMAL) BETWEEN '0' AND '100')
)
AND
(`object`.`property_collection_id`='2') GROUP BY `object`.`id`
How should I create this query?
Your query must reflect the dependency of the tables. Something like
SELECT ...
FROM object o
INNER JOIN property_collections pc ON pc.id = o.property_collection_id
INNER JOIN property p ON p.property_collection_id = pc.id
INNER JOIN property_value pv ON pv.property_id = p.id
WHERE ...
And, as Amdixon already pointed out, you probably meant to say
WHERE pv.value IN ('1','7')
As the original AND condition would always be false. However, I suspect, that you would have to check on the property_collection_id first, in order to make sure, which product type you are actually looking at. Whithout furher data it is impossible to come up with a likely solution.
Related
i have table actions (30 rows) and passed_actions(10k rows)
actions table:
CREATE TABLE `actions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`category_id` int(10) unsigned NOT NULL,
`author_id` int(10) unsigned NOT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'need for url',
`about` longtext COLLATE utf8_unicode_ci,
`image` text COLLATE utf8_unicode_ci,
`page_title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`page_description` varchar(512) COLLATE utf8_unicode_ci DEFAULT NULL,
`active` tinyint(1) NOT NULL DEFAULT '0',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `actions_slug_unique` (`slug`),
KEY `actions_author_id_foreign` (`author_id`),
KEY `actions_category_id_foreign` (`category_id`),
CONSTRAINT `actions_author_id_foreign` FOREIGN KEY (`author_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `actions_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
and passed_actions (~9500 rows)
CREATE TABLE `passed_actions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`action_id` int(10) unsigned NOT NULL,
`hash` varchar(6) COLLATE utf8_unicode_ci NOT NULL,
`public` tinyint(1) NOT NULL DEFAULT '1',
`successfully_passed` tinyint(1) NOT NULL DEFAULT '0',
`started_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `passed_actions_hash_unique` (`hash`),
KEY `passed_actions_user_id_foreign` (`user_id`),
KEY `passed_actions_action_id_foreign` (`action_id`),
CONSTRAINT `passed_actions_action_id_foreign` FOREIGN KEY (`action_id`) REFERENCES `actions` (`id`) ON DELETE CASCADE,
CONSTRAINT `passed_actions_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=25733 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
this query takes 0.3-0.5s:
select
actions.*
from actions
left join passed_actions.action_id = actions.id
group by actions.id
order by count(passed_actions.id) DESC
this affects to response time of my api...
why is this happening? i think that 10k rows is not a large table...
i use default mysql config. My server is 1gb ram and 1 cpu (digital ocean droplet)
Your query is actually reasonable fast. Sometimes a correlated subquery can help:
select a.*
from actions a
order by (select count(*) from passed_actions pa where pa.action_id = a.id) desc;
This can use the index you have defined on passed_actions(action_id).
If all you want off the second table is the count for sorting, as appears to be the case, try (untested, sorry):
select
actions.*
from actions
left join (select action_id, count(*) as passed_count from passed_actions group by action_id) p on actions.action_id = p.action_id
order by passed_count DESC
(I can't see where tests.id is coming from, I'm afraid.)
1- Rebuild the index and update statistics
2- Select Only the column you want to use
3- run this query in a new query and hit "Right click" and Click on "Display Estimated Execution Plan" and view the Missing Index Details and build the required index and run the query again
I have the following schema:
CREATE TABLE `news` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`news_category_id` int(10) unsigned NOT NULL,
`news_type_id` int(10) unsigned NOT NULL,
`news_pictures_main_id` int(10) unsigned DEFAULT NULL,
`title` tinytext COLLATE latin1_general_ci,
`body` text COLLATE latin1_general_ci,
`tmstp` timestamp NULL DEFAULT NULL,
`subcategory` varchar(64) COLLATE latin1_general_ci DEFAULT NULL,
`source` varchar(128) COLLATE latin1_general_ci DEFAULT NULL,
`old_id` int(10) unsigned DEFAULT NULL,
`tags` text COLLATE latin1_general_ci,
PRIMARY KEY (`id`),
KEY `news_time_idx` (`tmstp`),
KEY `fk_news_news_pictures1` (`news_pictures_main_id`),
KEY `fk_news_news_category1` (`news_category_id`),
KEY `fk_news_news_type1` (`news_type_id`),
CONSTRAINT `fk_news_news_category1` FOREIGN KEY (`news_category_id`) REFERENCES `news_category` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_news_news_pictures1` FOREIGN KEY (`news_pictures_main_id`) REFERENCES `news_pictures` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT `fk_news_news_type1` FOREIGN KEY (`news_type_id`) REFERENCES `news_type` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB
CREATE TABLE `news_pictures` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`path` text COLLATE latin1_general_ci,
`description` text COLLATE latin1_general_ci,
`author` varchar(45) COLLATE latin1_general_ci DEFAULT NULL,
`news_id` int(10) unsigned DEFAULT NULL,
`temp_id` varchar(40) COLLATE latin1_general_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `filename_old_id_unq` (`path`(20),`temp_id`(6)),
KEY `fk_news_pictures_news1` (`news_id`),
KEY `temp_id_idx` (`temp_id`(8)),
CONSTRAINT `fk_news_pictures_news1` FOREIGN KEY (`news_id`) REFERENCES `news` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB
CREATE TABLE `news_category` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(45) COLLATE latin1_general_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
CREATE TABLE `news_type` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(45) COLLATE latin1_general_ci DEFAULT NULL,
`slug` varchar(45) COLLATE latin1_general_ci DEFAULT NULL,
PRIMARY KEY (`id`)
KEY `news_type_slug_idx` (`slug`)
) ENGINE=InnoDB
From that, there is derived the following view:
CREATE OR REPLACE VIEW `news_full` AS select `n`.`id` AS `id`,
`n`.`title` AS `title`,
`n`.`body` AS `body`,
`n`.`tmstp` AS `tmstp`,
`n`.`subcategory` AS `subcategory`,
`n`.`source` AS `source`,
`n`.`old_id` AS `old_id`,
`n`.`news_type_id` AS `news_type_id`,
`n`.`tags` AS `tags`,
`nt`.`name` AS `news_type_name`,
`nt`.`slug` AS `news_type_slug`,
`n`.`news_pictures_main_id` AS `news_pictures_main_id`,
`np`.`path` AS `news_pictures_main_path`,
`np`.`description` AS `news_pictures_main_description`,
`np`.`author` AS `news_pictures_main_author`,
`np`.`temp_id` AS `news_pictures_main_temp_id`,
`n`.`news_category_id` AS `news_category_id`,
`nc`.`name` AS `news_category_name`
from (((`news` `n`
left join `news_pictures` `np` on((`n`.`news_pictures_main_id` = `np`.`id`)))
join `news_category` `nc` on((`n`.`news_category_id` = `nc`.`id`)))
join `news_type` `nt` on((`n`.`news_type_id` = `nt`.`id`)));
However, if I try to run the following query:
select * from news_full order by tmstp limit 100
I get the following execution plan (please click on the image to expand it):
Notice the Using temporary; Using filesort field in the first step. But this is weird, because tmstp field is indexed on the base table.
First I thought this was due the left join on the view, but I've changed it to inner join and I got the same results.
Edit
As #Michael-sqlbot cleverly noticed, the query optimizer is inverting the order of the base tables, putting news_category (nc) first.
If I change the query that creates the view to use only LEFT JOINs it seems to work:
The execution times, as expected, as blatantly different:
Not satisfied, I created another view with the original query, adding the STRAIGHT_JOIN statement. So, the query plan comes as follows:
So, it's not using the index.
However, if I run the plan for the base query adding the same ORDER BY and LIMIT clauses, it does uses the index:
(Not an answer, but some other issues to bring up...)
UNIQUE KEY `filename_old_id_unq` (`path`(20),`temp_id`(6))
That constrains the first 20 characters of path, together with the first 6 characters of temp_id to be unique across the table. Did you really want that?
I suspect the optimizer will never use both columns of that index. (In general, prefixing is useless.)
And...
`title` tinytext COLLATE latin1_general_ci
Change to VARCHAR(255). There are disadvantages of TINYTEXT and perhaps no advantages.
First query:
SELECT
u.id,
u.first_name,
u.last_name,
u.tazkera_id,
cu.relation_type,
CASE csp.`shift`
WHEN '1' THEN 'Morning'
WHEN '2' THEN 'Afternoon'
else 'Neither'
END AS shift
FROM `course_user` as cu
LEFT JOIN `courses` as c ON c.id = cu.`course_id`
LEFT JOIN (
select users.id,
users.first_name,
users.last_name,
users.tazkera_id
FROM `users`
) as u ON u.id = cu.`user_id`
left JOIN `course_schedule_prefs` as csp ON csp.`user_id` = cu.`user_id`
Where cu.relation_type = 1 group by cu.`user_id`;
Second Query:
SELECT
u.id,
u.first_name,
u.last_name,
u.tazkera_id,
cu.relation_type,
CASE csp.`shift`
WHEN '1' THEN 'Morning'
WHEN '2' THEN 'Afternoon'
else 'Neither'
END AS shift
FROM `course_user` as cu
LEFT JOIN `courses` as c ON c.id = cu.`course_id`
LEFT JOIN users as u ON u.id = cu.`user_id`
left JOIN `course_schedule_prefs` as csp ON csp.`user_id` = cu.`user_id`
Where cu.relation_type = 1 group by cu.`user_id`;
Tables :
users:
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`first_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`last_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`password` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`is_password_reset` int(11) NOT NULL DEFAULT '0',
`login_ip` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`login_date` datetime DEFAULT NULL,
`last_login_ip` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`last_login_date` datetime DEFAULT NULL,
`is_email_address_verified` int(11) NOT NULL DEFAULT '0',
`failed_login_attempts` int(11) DEFAULT NULL,
`last_failed_login_date` datetime DEFAULT NULL,
`tazkera_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`province_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`district_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`village` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`home_address` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`promote_unique_user_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`remember_token` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`forgot_token` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`email_confirm_token` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`district_other` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`father_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`training_provider_id` int(10) unsigned DEFAULT NULL,
`is_profile_completed` int(11) DEFAULT NULL,
`active` int(11) DEFAULT '1',
`picture` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`small_picture` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`last_poll` bigint(20) DEFAULT NULL,
`last_password_reset` date DEFAULT NULL,
`presence_status` int(11) DEFAULT NULL,
`auto_presence` int(11) NOT NULL DEFAULT '0',
`last_activity` timestamp NULL DEFAULT NULL,
`trash` tinyint(4) NOT NULL DEFAULT '0',
`login_approval` tinyint(4) NOT NULL DEFAULT '0',
`lang` varchar(2) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'en',
`cover` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`small_cover` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`forum_reputation` int(11) NOT NULL DEFAULT '0',
`forum_suspension_date` date DEFAULT NULL,
`forum_status` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `users_email_unique` (`email`),
KEY `users_training_provider_id_foreign` (`training_provider_id`),
CONSTRAINT `users_training_provider_id_foreign` FOREIGN KEY (`training_provider_id`) REFERENCES `training_providers` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1000038 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
course_users:
CREATE TABLE `course_user` (
`course_id` int(10) unsigned DEFAULT NULL,
`user_id` int(10) unsigned DEFAULT NULL,
`relation_type` int(11) DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
KEY `course_user_course_id_foreign` (`course_id`),
KEY `course_user_user_id_foreign` (`user_id`),
CONSTRAINT `course_user_course_id_foreign` FOREIGN KEY (`course_id`) REFERENCES `courses` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `course_user_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
courses;
CREATE TABLE `courses` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`curriculum_id` int(10) unsigned DEFAULT NULL,
`training_center_id` int(10) unsigned DEFAULT NULL,
`training_coordinator_id` int(10) unsigned DEFAULT NULL,
`focal_point_id` int(10) unsigned DEFAULT NULL,
`start_date` date DEFAULT NULL,
`end_date` date DEFAULT NULL,
`start_time` time DEFAULT NULL,
`end_time` time DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`shift` int(11) DEFAULT NULL,
`stage_id` int(10) unsigned DEFAULT NULL,
`stream_id` int(10) unsigned DEFAULT NULL,
`unit_id` int(10) unsigned DEFAULT NULL,
`master_trainer_id` int(10) unsigned DEFAULT NULL,
`status` int(11) DEFAULT NULL,
`pre_test_generated` int(11) NOT NULL DEFAULT '0',
`post_test_generated` int(11) NOT NULL DEFAULT '0',
`conduct_days` int(11) DEFAULT NULL,
`no` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`evaluation_available` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `courses_curriculum_id_foreign` (`curriculum_id`),
KEY `courses_training_coordinator_id_foreign` (`training_coordinator_id`),
KEY `courses_focal_point_id_foreign` (`focal_point_id`),
KEY `courses_stage_id_foreign` (`stage_id`),
KEY `courses_stream_id_foreign` (`stream_id`),
KEY `courses_unit_id_foreign` (`unit_id`),
KEY `courses_master_trainer_id_foreign` (`master_trainer_id`),
KEY `courses_training_center_id_foreign` (`training_center_id`),
CONSTRAINT `courses_curriculum_id_foreign` FOREIGN KEY (`curriculum_id`) REFERENCES `curriculums` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `courses_focal_point_id_foreign` FOREIGN KEY (`focal_point_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `courses_master_trainer_id_foreign` FOREIGN KEY (`master_trainer_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `courses_stage_id_foreign` FOREIGN KEY (`stage_id`) REFERENCES `stages` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `courses_stream_id_foreign` FOREIGN KEY (`stream_id`) REFERENCES `streams` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `courses_training_center_id_foreign` FOREIGN KEY (`training_center_id`) REFERENCES `training_centers` (`id`),
CONSTRAINT `courses_training_coordinator_id_foreign` FOREIGN KEY (`training_coordinator_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `courses_unit_id_foreign` FOREIGN KEY (`unit_id`) REFERENCES `units` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=139 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
course_schedule_prefs;
CREATE TABLE `course_schedule_prefs` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`cunduct_days` int(11) DEFAULT NULL,
`training_centers` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`shift` int(11) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`user_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `course_schedule_prefs_user_id_foreign` (`user_id`),
CONSTRAINT `course_schedule_prefs_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
My question is , which one of this query is good in term of performance and accuracy. Why ?
first query: I join directly with table; select all columns of table.
Second query: I used subquery inside of join; select only those element which I need it.
Note : My main question is about users table which is in join
There are too many possibilities here. There is no simple, 'straight', answer.
With LEFT JOIN (instead of JOIN), the Optimizer cannot start with the subquery, which it would usually like to do.
If there is a WHERE clause and it is useful for filtering rows, the Optimizer would like to start with the table that is filtered the most. Your WHERE clause smells like a flag, which is usually not very useful.
If a subquery has a GROUP BY or LIMIT, then it may significantly shrink the number of rows to work with. (Not in your case.) This might make the subquery approach better.
If cu has INDEX(relation_type, user_id), the Optimizer may decide that handling all of the WHERE and GROUP BY leads to the best bet, thereby starting with cu as the 'first' table.
In old versions of MySQL, subqueries in JOIN ( SELECT ... ) had no way of getting an index, thereby leading to inefficient table scans of that tmp table. In new versions (starting with 5.6, I think), the Optimizer takes the extra step (which costs something) to generate the optimal index ((id) in this case) after creating the tmp table and before JOINing it.
More
2 more table definitions needed.
Do you have the indexes I suggested?
InnoDB really needs a PRIMARY KEY - See many:many for advice on course_user.
I have a website that has two main queries to the database which are pretty slow the first time they are run, after a bit of testing it appears to be an issue with MySQL. If I run the query directly in Sequal Pro when I run a query the first time it can take up to 4 seconds to run but running the same query again takes ~60ms, the query time is about the same locally as on our server, which make me think its not a server issue.
Not totally sure that increasing the buffer pool size will help too much as the number of potential query combinations is probably around 800K.
The tables in the database are innodb, both queries access the same table that has 52K records, most of the information I need has been grouped together into a 'searchfield' field which is indexed.
Only fields used in queries or are primary/foreign keys are being indexed.
I have tried changing the inner joins to a select statement in the "where" of the main query but this doesn't make the query any faster.
The queries are
Query 1
SELECT
`item_attribute`.`attribute_id` AS `attribute_id`,
`attribute_value_id` AS `attribute_value_id`,
`collection_attribute`.`title` AS `ca_title`,
`collection_attribute`.`type` AS `ca_type`,
`collection_attribute`.`is_collapsible` AS `ca_is_collapsible`,
`collection_attribute`.`orderindex` AS `ca_orderindex`,
`collection_attribute`.`multi_select` AS `ca_multi_select`,
`item_attribute`.`item_id` AS `item_id`,
`product`.`id` AS `product_id`
FROM `item_attribute`
INNER JOIN `item` ON item.id = item_attribute.item_id
INNER JOIN `product` ON product.id = item.product_id
INNER JOIN `collection_attribute` ON item_attribute.attribute_id = collection_attribute.attribute_id
INNER JOIN `attribute_value` ON attribute_value.id = item_attribute.attribute_value_id
WHERE ((`product`.`searchfilter` LIKE '%c:35∆%') AND (`collection_attribute`.`collection_id`='35')) AND (`attribute_value`.`active`=1)
GROUP BY `attribute_value_id`
Query 2
SELECT DISTINCT `item_attribute`.`attribute_id` AS `attribute_id`,
GROUP_CONCAT(item_attribute.attribute_value_id SEPARATOR \"-\") AS `attribute_value`,
GROUP_CONCAT(attribute_value.title SEPARATOR \" - \") AS `title`
FROM `item_attribute`
LEFT JOIN `item` ON item.id = item_attribute.item_id
LEFT JOIN `attribute` ON attribute.id = item_attribute.attribute_id
LEFT JOIN `attribute_value` ON attribute_value.id = attribute_value_id
WHERE (`item`.`product_id`='894') AND (`attribute`.`is_option`=1)
GROUP BY `attribute_id`, `item_id`
Table Structure
CREATE TABLE `product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`slug` varchar(255) NOT NULL,
`sku` varchar(20) NOT NULL,
`active` tinyint(1) DEFAULT '0',
`orderindex` int(2) DEFAULT '-1',
`search` varchar(255) DEFAULT NULL,
`searchfilter` varchar(255) DEFAULT NULL,
`created_at` int(11) DEFAULT NULL,
`updated_at` int(11) DEFAULT NULL,
`protected` tinyint(1) DEFAULT '0',
`description` varchar(512) DEFAULT NULL,
`type_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `product_ibfk_1` (`type_id`),
KEY `searchfilter` (`searchfilter`),
CONSTRAINT `product_ibfk_1` FOREIGN KEY (`type_id`) REFERENCES `attribute_value` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1882 DEFAULT CHARSET=utf8;
--
CREATE TABLE `collection_attribute` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`collection_id` int(11) DEFAULT NULL,
`attribute_id` int(11) DEFAULT NULL,
`title` varchar(512) NOT NULL,
`slug` varchar(512) NOT NULL,
`created_at` int(11) DEFAULT NULL,
`updated_at` int(11) DEFAULT NULL,
`active` tinyint(1) DEFAULT '0',
`orderindex` int(2) DEFAULT '-1',
`search` varchar(255) DEFAULT NULL,
`searchfilter` varchar(255) DEFAULT NULL,
`protected` tinyint(1) DEFAULT '0',
`is_collapsible` tinyint(1) DEFAULT '0',
`type` enum('icon','checkbox','checkboxIcon','image') DEFAULT NULL,
`multi_select` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `collection_attribute_ibfk_1` (`collection_id`),
KEY `collection_attribute_ibfk_2` (`attribute_id`),
CONSTRAINT `collection_attribute_ibfk_1` FOREIGN KEY (`collection_id`) REFERENCES `collection` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `collection_attribute_ibfk_2` FOREIGN KEY (`attribute_id`) REFERENCES `attribute` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=140 DEFAULT CHARSET=utf8;
--
CREATE TABLE `item` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`slug` varchar(255) NOT NULL,
`title` varchar(255) NOT NULL,
`pattern_code` varchar(32) NOT NULL,
`tom_code` varchar(32) NOT NULL,
`navision_code` varchar(32) NOT NULL,
`description` varchar(255) DEFAULT NULL,
`active` tinyint(1) DEFAULT '0',
`orderindex` int(2) DEFAULT '-1',
`created_at` int(11) DEFAULT NULL,
`updated_at` int(11) DEFAULT NULL,
`search` varchar(1024) DEFAULT NULL,
`searchfilter` varchar(255) DEFAULT NULL,
`product_id` int(11) DEFAULT NULL,
`protected` tinyint(1) DEFAULT '0',
`pattern_series` varchar(255) DEFAULT NULL,
`pattern_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `item_ibfk_1` (`product_id`),
KEY `searchfilter` (`searchfilter`),
KEY `product_id` (`product_id`),
KEY `pattern_id` (`pattern_id`),
CONSTRAINT `item_ibfk_1` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `item_ibfk_2` FOREIGN KEY (`pattern_id`) REFERENCES `pattern_series` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=50060 DEFAULT CHARSET=utf8;
--
CREATE TABLE `item_attribute` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`item_id` int(11) NOT NULL,
`attribute_id` int(11) NOT NULL,
`attribute_value_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `attribute_id` (`attribute_id`),
KEY `attribute_value_id` (`attribute_value_id`),
KEY `item_id` (`item_id`),
CONSTRAINT `item_attribute_ibfk_1` FOREIGN KEY (`attribute_id`) REFERENCES `attribute` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `item_attribute_ibfk_2` FOREIGN KEY (`attribute_value_id`) REFERENCES `attribute_value` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `item_attribute_ibfk_3` FOREIGN KEY (`item_id`) REFERENCES `item` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=857111 DEFAULT CHARSET=utf8;
--
CREATE TABLE `attribute_value` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`slug` varchar(255) NOT NULL,
`code` varchar(255) DEFAULT NULL,
`title` varchar(255) NOT NULL,
`active` tinyint(1) DEFAULT '0',
`orderindex` int(2) DEFAULT '-1',
`created_at` int(11) DEFAULT NULL,
`updated_at` int(11) DEFAULT NULL,
`search` varchar(255) DEFAULT NULL,
`searchfilter` varchar(255) DEFAULT NULL,
`attribute_id` int(11) DEFAULT NULL,
`protected` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3471 DEFAULT CHARSET=utf8;
--
CREATE TABLE `attribute` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`slug` varchar(255) NOT NULL,
`code` varchar(255) DEFAULT NULL,
`is_option` tinyint(1) DEFAULT '0',
`searches` tinyint(1) DEFAULT '0',
`option_type` enum('dropdown','switch','fingersizes') DEFAULT NULL,
`option_label` varchar(32) DEFAULT NULL,
`active` tinyint(1) DEFAULT '0',
`orderindex` int(2) DEFAULT '-1',
`created_at` int(11) DEFAULT NULL,
`updated_at` int(11) DEFAULT NULL,
`search` varchar(255) DEFAULT NULL,
`searchfilter` varchar(255) DEFAULT NULL,
`protected` tinyint(1) DEFAULT '0',
`option_requires` int(11) DEFAULT NULL,
`option_depends` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `is_option` (`is_option`)
) ENGINE=InnoDB AUTO_INCREMENT=47 DEFAULT CHARSET=utf8;
Any suggestions on how to improve the initial query time would be great
Thanks in advance :)
-- EDIT --
EXPLAIN SELECT query 1
EXPLAIN SELECT query 2
The first time you perform a query after starting the server, nothing is cache, so the query needs to fetch stuff from disk. All subsequent queries that access the same parts of the same tables will be much faster because of caching. This is "normal".
If you have the "Query cache" enable (it is probably enabled by default), then the second time you run exactly the same query, it will instantly find the result from the Query cache. By "exactly" I mean that not so much as a blank space has changed. Nearly all "production" servers are better off turning off the Query cache.
innodb_buffer_pool_size should be about 70% of available RAM. Changing the value won't affect a SELECT against a cold cache, but might help/hurt subsequent runs. This does not seem to be relevant in your case, since the second run was quite fast.
Please provide EXPLAIN SELECT ... so we can see how the optimizer decided to execute them.
LIKE '%c:35∆%' -- cannot use an index because of the leading wild card.
What is item_ids?
item_attribute is an EAV schema pattern. It sucks. Both the queries are ugly, and scalability hurts. It may help some to get rid of the id and make a compound PRIMARY KEY from a suitable combination of the other fields. The hope is to use the PRIMARY KEY which is clustered with the data instead of having to bounce from the secondary key. More discussion of EAV.
Assuming this has low cardinality, the index will probably never be used:
KEY is_option (is_option)
I want to merge two tables with same structure from two different schemas. Right now I am doing this through following query:
INSERT INTO schema1.table1(col1,col2)
SELECT col1,col2
FROM schema2.table1;
This query merges two tables fine into one but foreign keys are not updated. They are the same as in original table. So is there any way to do it.
CREATE TABLE `research_delta`.`source` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`url` varchar(500) COLLATE utf8_bin NOT NULL,
`createdOn` datetime NOT NULL,
`modifiedOn` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`isDeleted` tinyint(4) NOT NULL DEFAULT '0',
`structure` mediumblob,
`firstRunStatus` varchar(50) COLLATE utf8_bin NOT NULL DEFAULT '0',
`isMaster` tinyint(4) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='All the sources supported by the system';
CREATE TABLE `research_delta`.`sourcetagitem` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`source` bigint(20) DEFAULT NULL,
`tagItem` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fk_source_sourcetagitem` (`source`),
KEY `fk_tagItem_sourcetagitem` (`tagItem`),
CONSTRAINT `fk_source_sourcetagitem` FOREIGN KEY (`source`) REFERENCES `source` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fk_tagItem_sourcetagitem` FOREIGN KEY (`tagItem`) REFERENCES `tagitem` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=287 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE `research_delta`.`tagitem` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(100) COLLATE utf8_bin NOT NULL,
`description` varchar(1000) COLLATE utf8_bin DEFAULT NULL COMMENT 'this field will contain any description details about the type of category or tag..',
`parentId` bigint(20) DEFAULT NULL COMMENT 'if the category or tag in subject to be under any other catefory or tag then this field will contain the id of the category that it is under.',
`createdOn` datetime DEFAULT NULL,
`modifiedOn` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`isDeleted` tinyint(4) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=286 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='this table represents the tags and categories supported by a';
When i merge two tagitem table from different schema and then merge sourcetagitem table the foreign key i.e tagitem should be updated with updated tagitem id after merging.
Thanks,
Do actually need to merge them, or can you use a union when you want to query the data?
(SELECT * FROM schema1.table1) UNION (SELECT * FROM schema2.table1)
Or create a view in the same way...
CREATE VIEW view1 AS (SELECT * FROM schema1.table1) UNION (SELECT * FROM schema2.table1);
Then select whatever you're interested in from that
SELECT col1 FROM vv;