I have the following MySQL table:
CREATE TABLE `my_data` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`tstamp` int(10) unsigned NOT NULL DEFAULT '0',
`name` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
I want to optimise for the following SELECT query only:
SELECT `id`, `name` FROM `my_data` ORDER BY `name` ASC
Will adding the following index increase performance, regardless of the size of the table?
CREATE INDEX `idx_name_id` ON `my_data` (`name`,`id`);
An EXPLAIN query suggests it would, but I have no quick way of testing with a large data set.
Yes it would. Even though you are still doing a full table scan, having the index will make the sort operation (due to the order by) unnecessary.
But it will also add overhead to inserts and delete statements!
Index are usefull when you use the column in the where clause, or when the column is a part of the condition for a link between two tables. See MySQL Doc
Related
I have a table defined as follows:
| book | CREATE TABLE `book` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`provider_id` int(10) unsigned DEFAULT '0',
`source_id` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
`title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`description` longtext COLLATE utf8_unicode_ci,
PRIMARY KEY (`id`),
UNIQUE KEY `provider` (`provider_id`,`source_id`),
KEY `idx_source_id` (`source_id`),
) ENGINE=InnoDB AUTO_INCREMENT=1605425 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
when there are about 10 concurrent read with following sql:
SELECT * FROM `book` WHERE (provider_id = '1' AND source_id = '1037122800') ORDER BY `book`.`id` ASC LIMIT 1
it becomes slow, it takes about 100 ms.
however if I changed it to
SELECT * FROM `book` WHERE (provider_id = '1' AND source_id = '221630001') LIMIT 1
then it is normal, it takes several ms.
I don't understand why adding order by id makes query much slower? could anyone expain?
Try to add desired columns (Select Column Name,.. ) instead of * or Refer this.
Why is my SQL Server ORDER BY slow despite the ordered column being indexed?
I'm not a mysql expert, and not able to perform a detailed analysis, but my guess would be that because you are providing values for the UNIQUE KEY in the WHERE clause, the engine can go and fetch that row directly using an index.
However, when you ask it to ORDER BY the id column, which is a PRIMARY KEY, that changes the access path. The engine now guesses that since it has an index on id, and you want to order by id, it is better to fetch that data in PK order, which will avoid a sort. In this case though, it leads to a slower result, as it has to compare every row to the criteria (a table scan).
Note that this is just conjecture. You would need to EXPLAIN both statements to see what is going on.
This is a pretty basic question, but I'm confused by what I'm reading in various places. I have a simple table that doesn't contain a huge amount of data (less than 500 rows for any given db is typical) A typical query against this table looks like :
select system_fields.name from system_fields where system_fields.form_id=? and system_fields.field_id=?
My question is, should I have a separate index for form_id and one for field_id, or should I be creating an index on a combination of those two fields? I've never really done anything with multi-column indexes before.
CREATE TABLE IF NOT EXISTS `system_fields` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`field_id` int(11) NOT NULL,
`form_id` int(11) NOT NULL,
`name` varchar(50) NOT NULL,
`reference_field_id` varchar(1000) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `field_id` (`field_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=293 ;
If you are always going to query by these two fields, then add a multi-column index.
I'll also point out that if you're going to have < 500 rows in the table, your index may not even get used. Any performance difference with or without an index on a 500-row table will be negligible.
Here's a bit more (good) reading:
https://www.percona.com/blog/2014/01/03/multiple-column-index-vs-multiple-indexes-with-mysql-56/
There is a structure:
CREATE TABLE IF NOT EXISTS `categories` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`parent_id` int(11) unsigned NOT NULL DEFAULT '0',
`title` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Query_1:
SELECT * FROM `categories` WHERE `id` = 1234
Query_2:
SELECT * FROM `categories` WHERE `id` = 1234 LIMIT 1
I need to get just one row. Since we apply WHERE id=1234 (finding by PRIMARY KEY) obviously that row with id=1234 is only one in whole table.
After MySQL has found the row, whether engine to continue the search when using Query_1?
Thanks in advance.
Look at this SQLFiddle: http://sqlfiddle.com/#!2/a8713/4 and especially View Execution Plan.
You see, that MySQL recognizes the predicate on a PRIMARY column and therefore it does not matter if you add LIMIT 1 or not.
PS: A little more explanation: Look at the column rows of the Execution Plan. The number there is the amount of columns, the query engine thinks, it has to examine. Since the columns content is unique (as it's a primary key), this is 1. Compare it to this: http://sqlfiddle.com/#!2/9868b/2 same schema but without primary key. Here rows says 8. (The execution plan is explained in the German MySQL reference, http://dev.mysql.com/doc/refman/5.1/en/explain.html the English one is for some reason not so detailed.)
So I've got a table with all users, and their values. And I want to order them after how much "money" they got. The problem is that they have money in two seperate fields: users.money and users.bank.
So this is my table structure:
CREATE TABLE IF NOT EXISTS `users` (
`id` int(4) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(54) COLLATE utf8_swedish_ci NOT NULL,
`money` bigint(54) NOT NULL DEFAULT '10000',
`bank` bigint(54) NOT NULL DEFAULT '10000',
PRIMARY KEY (`id`),
KEY `users_all_money` (`money`,`bank`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_swedish_ci AUTO_INCREMENT=100 ;
And this is the query:
SELECT id, (money+bank) AS total FROM users FORCE INDEX (users_all_money) ORDER BY total DESC
Which works fine, but when I run EXPLAIN it shows "Using filesort", and I'm wondering if there is any way to optimize it?
Because you want to sort by a derived value (one that must be calculated for each row) MySQL can't use the index to help with the ordering.
The only solution that I can see would be to create an additional total_money or similar column and as you update money or bank update that value too. You could do this in your application code or it would be possible to do this in MySQL with triggers too if you wanted.
I'm profiling a web application, trying to cut on unnecessary delays on queries and I found one that seems to be simple, but take a lot of time for execute.
Using EXPLAIN I get the following messages:
Using where; Using temporary; Using filesort
This is the query:
SELECT `bt`.`id_brand`
FROM `brands_translation` AS `bt`
WHERE bt.code_language = 'es'
GROUP BY `bt`.`id_brand`
ORDER BY `bt`.`name` ASC
And the table definition:
CREATE TABLE IF NOT EXISTS `brands_translation` (
`id_brand` int(64) unsigned NOT NULL,
`code_language` varchar(3) NOT NULL,
`name` varchar(128) NOT NULL,
`link` varchar(255) default NULL,
`logo` varchar(255) default NULL,
`description` text NOT NULL,
KEY `id_brand` (`id_brand`),
KEY `code_language` (`code_language`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
I try to solve it creating indexes for every involved field with no result.
Any idea with that?
Thanks in advance
With a proper INDEX, MySQL may very well sort your results properly without the ORDER BY statement. You'll want to look into ORDER BY Optimization and LIMIT Optimization.
Otherwise, temporary and filesort usage come from a differing GROUP BY and ORDER BY. For this particular query, I would remove the indices you have in place and add this one:
ALTER TABLE `brands_translation` ADD INDEX (`code_language` , `id_brand` , `name` );
Of course, this may affect other queries throughout your project. Once that's done, change your query to:
SELECT `bt`.`id_brand`
FROM `brands_translation` AS `bt`
WHERE bt.code_language = 'es'
GROUP BY `bt`.`id_brand`, `bt`.`name`
ORDER BY `bt`.`id_brand`, `bt`.`name`
Realizing that you may not want to group by name, you can remove name from the GROUP BY statement, but that will give you using temporary again (since the GROUP and ORDER are different).
please look at the following...
drop table if exists brands_translation;
create table brands_translation
(
code_language varchar(3) not null,
id_brand int unsigned not null,
-- other fields here...
primary key(code_language, id_brand) -- clustered composite primary key (please !!)
)
engine=innodb;
why quote ` when you dont need to and sort out your data types pls.