I have a simple query like:
select count(*) from table
Can I speed up MySQL by doing:
select count(id) from table
Does this make any difference in terms of speed?
Best Regards,
Not if id is your primary key. * will simply map to your primary key. count(id) = count(*)
You can try to get the count from information_schema
select TABLE_ROWS from information_schema.tables
where TABLE_SCHEMA = '$db' and TABLE_NAME = '$tbl';
If id is your primary key, then no it will not, since MySql will use the primary key under the covers anyway. If id is not the primary key, then it may actually make the query slightly slower.
If id is "not null", no it won't make any difference.
If id is nullable, then it is a different query and will not (always) produce the same result.
Related
Query:
select * from table_a where col_a = 'value1' and col_b ='value2' order by id desc limit 1
Indexes:
col_a is indexed but col_b is not. col_a has a high cardinality (2M)
The entire table consists of 28M rows. No. of rows with col_a = 'value1' is 22,000.
The latest id is 28M. The latest rows with col_a = 'value1' has id somewhere in 25M-25.5M range.
Ideally it should scan these 22000 rows only and give us the result. But we have seen that mysql is scanning these 3M rows (28M - 25M primary key id value) and then returning the result.
Using mysql explain we found out that PRIMARY key is being used if the limit is set to less than 20 but after that user_id is being prioritised.
Has anyone else seen this behaviour? Is there any flag which can be set which will avoid scanning primary key ? (we don't want to use force index(col_a_idx). Is there any other way which could avoid this ?
Although How to hint the index to use in a MySQL select query? discusses the question as stated, I suggest that there is a better way to optimize the query.
INDEX(col_a, col_b, id)
(And Drop INDEX(col_a))
This will allow the query to run faster than forcing the use of PRIMARY KEY(id).
With this index, the Optimizer will automatically use it and look at exactly 1 row, not 28M, not 22000.
If col_b is TEXT, this new index won't work. Let's see SHOW CREATE TABLE, and please explain what type of stuff is in col_b.
Perhaps there is a datatype issue. Perhaps there is something goofy about your index(col_a).
I have following query which is taking time.
Mytable type is innodb and have primary key on field tsh_id
I have also added index on transaction_Id field
following is implementation inside my database stored procedure.
DECLARE lv_timestamp DATETIME(3);
SET #lv_timestamp = NOW(3);
IF(mycondition) then
SET #lv_Duration :=( SELECT UNIX_TIMESTAMP (#lv_timestamp) - UNIX_TIMESTAMP ( `changedon` )
FROM `MyTable`
WHERE transaction_Id = _transaction_Id
ORDER BY tsh_id DESC
LIMIT 1)
End if;
Please suggest any sort of improvement
Edit:
Explain to query says
"select_type":"SIMPLE",
"table":"MyTable",
"type":"ref",
"possible_keys":"IX_MyTable_Transaction",
"key":"IX_MyTable_Transaction",
"key_len":"98",
"ref":"const",
"rows":1,
"Extra":"Using where"
Make sure you have an index on MyTable.transaction_Id
Make sure you have the innodb_buffer_pool_size set to a decent value in your MySQL config
I am fairly certain your primary key is not a clustered key (its might be null or not unique or you changed it at one point), because that would explain this behaviour, at least if transaction_Id isn't unique either (and otherwise you wouldn't need limit 1).
To improve this specific query, you can create the following index:
create index ix_mytable_transaction_id_tsh_id on MyTable (transaction_id, tsh_id desc);
Use explain again.
If it doesn't use the new key, force it:
SELECT ... FROM `MyTable` force index(ix_mytable_transaction_id_tsh_id) ...
Some SQL servers allow for a generic statement such as ORDER BY PRIMARY KEY. I don't believe this works for MySQL, is there any such workaround that would allow for automated selects across multiple tables or does it require a lookup query to determine the primary key?
The workaround I have been working on involves calling a SHOW COLUMNS FROM before running the query. Is there a more efficient way of doing this? Can MySQL determine the primary key of a table during the select process?
Update: There is no official way of doing this in MySQL or SQL in general as Gordon pointed out. SAP has custom functionality for it. There are workarounds, such as working with SHOW COLUMNS FROM table or the information_schema as John pointed out.
MySQL generally pulls data out by insertion order which would be by primary key, but that aside you technically can do the same thing if you pull out the primary key column name and put it in an order by
SELECT whatever FROM table
ORDER BY
( SELECT `COLUMN_NAME`
FROM `information_schema`.`COLUMNS`
WHERE (`TABLE_SCHEMA` = 'dbName')
AND (`TABLE_NAME` = 'tableName')
AND (`COLUMN_KEY` = 'PRI')
);
For composite keys you can use this
SELECT whatever FROM table
ORDER BY
( SELECT GROUP_CONCAT(`COLUMN_NAME` SEPARATOR ', ')
FROM `information_schema`.`COLUMNS`
WHERE (`TABLE_SCHEMA` = 'dbName')
AND (`TABLE_NAME` = 'tableName')
AND (`COLUMN_KEY` = 'PRI')
);
Permission for information schema access from the DOCS
Each MySQL user has the right to access these tables, but
can see only the rows in the tables that correspond to objects for
which the user has the proper access privileges. In some cases (for
example, the ROUTINE_DEFINITION column in the
INFORMATION_SCHEMA.ROUTINES table), users who have insufficient
privileges see NULL. These restrictions do not apply for InnoDB
tables; you can see them with only the PROCESS privilege.
The same privileges apply to selecting information from
INFORMATION_SCHEMA and viewing the same information through SHOW
statements. In either case, you must have some privilege on an object
to see information about it.
SETUP:
CREATE TABLE some_stuff (
firstID INT,
secondID INT,
username varchar(55),
PRIMARY KEY (firstID, secondID)
) ;
QUERY:
SELECT GROUP_CONCAT(`COLUMN_NAME` SEPARATOR ', ')
FROM `information_schema`.`COLUMNS`
WHERE (`TABLE_SCHEMA` = 'dbName')
AND (`TABLE_NAME` = 'some_stuff')
AND (`COLUMN_KEY` = 'PRI');
OUTPUT:
+--------------------------------------------+
| GROUP_CONCAT(`COLUMN_NAME` SEPARATOR ', ') |
+--------------------------------------------+
| firstID, secondID |
+--------------------------------------------+
SAP does, indeed do this (http://help.sap.com/saphelp_nw04s/helpdata/en/fc/eb3a53358411d1829f0000e829fbfe/content.htm). SQL Server is also based on Sybase, and I don't think Sybase supported this functionality. There are many limitations on the syntax.
On a query on one table with a primary key, no explicit order by, and no where conditions, MySQL will generally return the results in primary key order. You cannot depend on this functionality, but it might be good enough for your system.
The big issue would be the use of indexes for the where clause. If there are no indexes on the table, you don't have to worry about it. If there are, you could possibly emulate the behavior with a materialized view:
select t.*
from (select t.*
from table t
) t
where <where clause here>;
Another option is to force the database engine to use the primary key index. You can do this by using a force index hint. The issue is that you need to know the name of the index.
I've got a huge table that looks like this:
CREATE TABLE `images` (
`image_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`data` mediumblob,
PRIMARY KEY (`user_id`,`image_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
on which I have to run a query which compresses the blob field. Will MySQL be able to use index with the following query:
UPDATE images SET data = COMPRESS(data) WHERE (user_id = ? AND image_id = ?) OR (user_id = ? AND image_id = ?) OR (...) OR (...);
I have to do it like this since there's no way I can update the whole table in a single query and I can't update by only using user_id.
EDIT: explain and update doesn't work, you guys know that, right?
Yes, your update will use the indexes on the table, since the only columns referred after the WHERE are the ones your primary key stands of.
If you are unsure of what a query will use, feel free to use the EXPLAIN command:
http://dev.mysql.com/doc/refman/5.0/en/explain.html
Use
EXPLAIN SELECT data FROM images
WHERE (user_id = ? AND image_id = ?)
OR (user_id = ? AND image_id = ?)
OR (...)
OR (...);
In your example, I'd expect MySQL to use the primary key as the clustered index. That means the index stores all columns, and is actually the only version of the data on disk.
So yes, it will use the index. With a condition including ors, I'd expect MySQL to scan the index (not seek.)
This is better to check using EXPLAIN than to speculate about too much.
Before even running EXPLAIN, issue ANALYZE TABLE to make sure that the query optimizer has the best chances to find an optimum query plan.
Yes, it will most probably use the index (unless your conditions have really low cardinality).
MySQL also supports this syntax:
(user_id, image_id) IN ((user1, image1), (user2, image2), (user3, image3))
but this one will not use the index (this is just an implementation flaw).
You can also use this query:
UPDATE (
SELECT user1 AS user_id, image1 AS image_id
UNION ALL
SELECT user2 AS user_id, image2 AS image_id
UNION ALL
SELECT user3 AS user_id, image3 AS image_id
) q
JOIN images i
ON (i.user_id, i.image_id) = (q.user_id, q.image_id)
SET i.data = COMPRESS(i.data)
which will also use the index.
I have id, member_id, topic_id fields. Sometimes I use id, sometimes member_id and sometimes topic_id in WHERE clauses. Can I add Indexes to all of them? Will it make it slower? I am new to MYSQL optimization stuff, so thank you.
Unused indexes won't make a SELECT slower, but each index you add will slow down INSERTs and UPDATEs.
The maximum number of indexes a MyISAM table can have is 64
In general, you would want a separate index on each field if you will be filtering your queries only on single fields, such as in the following case:
SELECT * FROM your_table WHERE id = ?;
SELECT * FROM your_table WHERE member_id = ?;
SELECT * FROM your_table WHERE topic_id = ?;
If the id field is the primary key, then that is probably already using a clustered index. Therefore it looks like you may want to try creating two separate non-clustered indexes on member_id and topic_id:
CREATE INDEX ix_your_table_member_id ON your_table (member_id);
CREATE INDEX ix_your_table_topic_id ON your_table (topic_id);
You may also be interested in researching the topic of covering indexes.