This is my SQL table, a huge table with around ~6kk rows.
CREATE TABLE `slots` (
`id` mediumint(8) UNSIGNED NOT NULL,
`uid` smallint(5) UNSIGNED NOT NULL,
`music_id` mediumint(8) UNSIGNED NOT NULL,
`finished` int(10) UNSIGNED NOT NULL DEFAULT '0',
`completed` tinyint(1) UNSIGNED NOT NULL DEFAULT '0',
`hidden` tinyint(1) UNSIGNED NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `slots`
ADD PRIMARY KEY (`id`),
ADD KEY `SEC_UNQ` (`uid`,`music_id`) USING BTREE;
ALTER TABLE `slots` MODIFY `id` mediumint(8) UNSIGNED NOT NULL AUTO_INCREMENT;
I'm looking frequently after uid, music_id and completed. For example:
SELECT `music_id` FROM `slots` WHERE `uid` = :uid AND `completed` = :completed;
and SELECT or UPDATE by uid and music_id
SELECT `music_id` FROM `slots` WHERE **`uid` = :uid AND `music_id` = :music_id**;
UPDATE `slots` SET xxx WHERE `uid` = :uid AND `music_id` = :music_id;
so the question is:
I have to create 3 indexes for all of the following columns: uid, music_id and completed or it's enough only for uid ?
..and which is better, single column index or multi-column indexes?
PS: I always have uid in WHERE statement
Thank you in advance
You can easy test it. A good index is always the best, also for small Type Boolean. It is realy easy to understand: If you have a large table mysql must read the hole table (FULL TABLE SCAN) to find a few row to update or delete. BUT MySQL can mostly only use one index per query. So a composite index is helpfull. and MySQL can also use them for single fields. Lets say you have a composite index on field (a,b,c) MySQL can use them in the WHERE Clause if only a , a and b or a and b and c , but not only on c or b.
Hier is a sample. there you can see how man rows MySQL must read and which index are used:
Drop Table and create a new
MariaDB []> DROP TABLE IF EXISTS mytable;
Query OK, 0 rows affected (0.29 sec)
MariaDB []>
MariaDB []> CREATE TABLE `mytable` (
-> `id` INT(11) UNSIGNED NOT NULL,
-> `a` INT(11) DEFAULT NULL,
-> `b` INT(11) DEFAULT NULL,
-> `c` INT(11) DEFAULT NULL,
-> PRIMARY KEY (`id`)
-> ) ENGINE=INNODB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.24 sec)
MariaDB []>
Insert 3000000 ROWS (only MariaDB)
MariaDB []> INSERT INTO mytable (id,a,b,c)
-> SELECT seq, (seq MOD 2), (seq MOD 3) , (seq MOD 4) FROM seq_0_to_3000000;
Query OK, 3000001 rows affected (15.66 sec)
Records: 3000001 Duplicates: 0 Warnings: 0
MariaDB []>
Test WHERE on field a - MySQL reads 2995634 rows
MariaDB []> EXPLAIN SELECT * FROM mytable WHERE a=1;
+------+-------------+---------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+------+---------------+------+---------+------+---------+-------------+
| 1 | SIMPLE | mytable | ALL | NULL | NULL | NULL | NULL | 2995634 | Using where |
+------+-------------+---------+------+---------------+------+---------+------+---------+-------------+
1 row in set (0.12 sec)
Add a Key on Field a
MariaDB []> ALTER TABLE mytable ADD KEY key_a (a);
Query OK, 0 rows affected (10.74 sec)
Records: 0 Duplicates: 0 Warnings: 0
Test the last query again (WHERE a) - MySQL reads only 1496635 rows
MariaDB []> EXPLAIN SELECT * FROM mytable WHERE a=1;
+------+-------------+---------+------+---------------+-------+---------+-------+---------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+------+---------------+-------+---------+-------+---------+-------+
| 1 | SIMPLE | mytable | ref | key_a | key_a | 5 | const | 1496635 | |
+------+-------------+---------+------+---------------+-------+---------+-------+---------+-------+
1 row in set (0.00 sec)
Test with WHERE on fiels a and b - 1496635 rows
MariaDB []> EXPLAIN SELECT * FROM mytable WHERE a=1 AND b=2;
+------+-------------+---------+------+---------------+-------+---------+-------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+------+---------------+-------+---------+-------+---------+-------------+
| 1 | SIMPLE | mytable | ref | key_a | key_a | 5 | const | 1496635 | Using where |
+------+-------------+---------+------+---------------+-------+---------+-------+---------+-------------+
1 row in set (0.00 sec)
Add key on field b
MariaDB []> ALTER TABLE mytable ADD KEY key_b (b);
Query OK, 0 rows affected (9.53 sec)
Records: 0 Duplicates: 0 Warnings: 0
Same test with a and b - same rows - only use key_a
MariaDB []> EXPLAIN SELECT * FROM mytable WHERE a=1 AND b=2;
+------+-------------+---------+------+---------------+-------+---------+-------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+------+---------------+-------+---------+-------+---------+-------------+
| 1 | SIMPLE | mytable | ref | key_a,key_b | key_a | 5 | const | 1496635 | Using where |
+------+-------------+---------+------+---------------+-------+---------+-------+---------+-------------+
1 row in set (0.00 sec)
Create index on a and b
MariaDB []> ALTER TABLE mytable ADD KEY key_ab (a,b);
Query OK, 0 rows affected (11.86 sec)
Records: 0 Duplicates: 0 Warnings: 0
Test with a and b - use key_ab and only 946702 rows read
MariaDB []> EXPLAIN SELECT * FROM mytable WHERE a=1 AND b=2;
+------+-------------+---------+------+--------------------+--------+---------+-------------+--------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+------+--------------------+--------+---------+-------------+--------+-------+
| 1 | SIMPLE | mytable | ref | key_a,key_b,key_ab | key_ab | 10 | const,const | 946702 | |
+------+-------------+---------+------+--------------------+--------+---------+-------------+--------+-------+
1 row in set (0.01 sec)
Test with field a,b and c -- kay_ab used and 946702 rows read
MariaDB []> EXPLAIN SELECT * FROM mytable WHERE a=1 AND b=2 AND c=3;
+------+-------------+---------+------+--------------------+--------+---------+-------------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+------+--------------------+--------+---------+-------------+--------+-------------+
| 1 | SIMPLE | mytable | ref | key_a,key_b,key_ab | key_ab | 10 | const,const | 946702 | Using where |
+------+-------------+---------+------+--------------------+--------+---------+-------------+--------+-------------+
1 row in set (0.00 sec)
Add Key on field a,b,c
MariaDB []> ALTER TABLE mytable ADD KEY key_abc (a,b,c);
Query OK, 0 rows affected (18.64 sec)
Records: 0 Duplicates: 0 Warnings: 0
Test with field a,b,c - key_abc used - and 511082 rows read
MariaDB []> EXPLAIN SELECT * FROM mytable WHERE a=1 AND b=2 AND c=3;
+------+-------------+---------+------+----------------------------+---------+---------+-------------------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+------+----------------------------+---------+---------+-------------------+--------+-------------+
| 1 | SIMPLE | mytable | ref | key_a,key_b,key_ab,key_abc | key_abc | 15 | const,const,const | 511082 | Using index |
+------+-------------+---------+------+----------------------------+---------+---------+-------------------+--------+-------------+
1 row in set (0.01 sec)
So the most effect is on composite index, but it also depends on your used querys.
Related
Let's say I have 2 tables - item_images and images.
When I run query
SELECT image_id FROM item_images WHERE item_id=1
I get image_id values 5, 6
When I run
DELETE FROM images WHERE id in (5, 6);
It also works and deletes these 2 rows.
But when I try to chain these 2 queries together, it fails with error 1175.
DELETE FROM images WHERE id in (SELECT image_id FROM item_images WHERE item_id=1);
Error Code:
1175. You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column To disable safe mode, toggle the option in Preferences -> SQL Editor and reconnect. 0.000 sec
id field is set as private key, not null.
Why does this happen if if id in WHERE is clearly private key?
Is the only way to go around this is to disable safe mode, or is there another way?
Thanks!
Assuming id column (images table) is always greater than zero (0):
mysql> SET SESSION SQL_SAFE_UPDATES := 1;
Query OK, 0 rows affected (0.00 sec)
mysql> DROP TABLE IF EXISTS `item_images`, `images`;
Query OK, 0 rows affected (0.09 sec)
mysql> CREATE TABLE IF NOT EXISTS `images` (
-> `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE IF NOT EXISTS `item_images` (
-> `item_id` BIGINT UNSIGNED NOT NULL,
-> `image_id` BIGINT UNSIGNED NOT NULL
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO `images`
-> VALUES (NULL), (NULL), (NULL),
-> (NULL), (NULL), (NULL);
Query OK, 6 rows affected (0.00 sec)
Records: 6 Duplicates: 0 Warnings: 0
mysql> INSERT INTO `item_images`
-> (`item_id`, `image_id`)
-> VALUES (1, 5), (1, 6), (2, 1),
-> (2, 3), (3, 2), (4, 2);
Query OK, 6 rows affected (0.00 sec)
Records: 6 Duplicates: 0 Warnings: 0
mysql> SELECT `image_id`
-> FROM `item_images`
-> WHERE `item_id` = 1;
+----------+
| image_id |
+----------+
| 5 |
| 6 |
+----------+
2 rows in set (0.00 sec)
mysql> DELETE
-> FROM `images`
-> WHERE `id` IN (SELECT `image_id`
-> FROM `item_images`
-> WHERE `item_id` = 1);
ERROR 1175 (HY000): You are using safe update mode and you tried to update
a table without a WHERE that uses a KEY column
mysql> DELETE
-> FROM `images`
-> WHERE `id` > 0 AND
-> `id` IN (SELECT `image_id`
-> FROM `item_images`
-> WHERE `item_id` = 1);
Query OK, 2 rows affected (0.01 sec)
See db-fiddle.
UPDATE
In the first DELETE the index (key) is not reached.
mysql> SET SESSION SQL_SAFE_UPDATES := 0;
Query OK, 0 rows affected (0.00 sec)
mysql> EXPLAIN DELETE
-> FROM `images`
-> WHERE `id` IN (SELECT `image_id`
-> FROM `item_images`
-> WHERE `item_id` = 1);
+----+--------------------+-------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+-------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | DELETE | images | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100.00 | Using where |
| 2 | DEPENDENT SUBQUERY | item_images | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 16.67 | Using where |
+----+--------------------+-------------+------------+------+---------------+------+---------+------+------+----------+-------------+
2 rows in set (0.00 sec)
mysql> EXPLAIN DELETE
-> FROM `images`
-> WHERE `id` > 0 AND
-> `id` IN (SELECT `image_id`
-> FROM `item_images`
-> WHERE `item_id` = 1);
+----+--------------------+-------------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+-------------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| 1 | DELETE | images | NULL | range | PRIMARY | PRIMARY | 8 | const | 6 | 100.00 | Using where |
| 2 | DEPENDENT SUBQUERY | item_images | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 16.67 | Using where |
+----+--------------------+-------------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
2 rows in set (0.01 sec)
See db-fiddle.
My problem is: simple select query takes a long time (3 minutes).
Structure:
mysql> show create table seventhcont_exceptionreport;
seventhcont_exceptionreport | CREATE TABLE `seventhcont_exceptionreport` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`body_html` longtext NOT NULL,
`datetime_created` datetime NOT NULL,
`subject` varchar(256) NOT NULL,
`host` varchar(128) NOT NULL,
`exc_value` varchar(512) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=74607 DEFAULT CHARSET=utf8 |
Rows count:
mysql> select count(*) from seventhcont_exceptionreport;
+----------+
| count(*) |
+----------+
| 7064 |
+----------+
1 row in set (0.00 sec)
Query 1 (normal):
mysql> select id, datetime_created from seventhcont_exceptionreport order by id LIMIT 100 OFFSET 6000;
...
100 rows in set (0.30 sec)
Query 2 (very slow):
mysql> select id, datetime_created from seventhcont_exceptionreport order by id LIMIT 100 OFFSET 7000;
...
63 rows in set (3 min 40.56 sec)
!!! 3 minutes and 40 sec.
Why?
UPDATE
Explain for query 1:
mysql> EXPLAIN select id, datetime_created from seventhcont_exceptionreport order by id LIMIT 100 OFFSET 6000;
+----+-------------+-----------------------------+-------+---------------+---------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------------------------+-------+---------------+---------+---------+------+------+-------+
| 1 | SIMPLE | seventhcont_exceptionreport | index | NULL | PRIMARY | 4 | NULL | 6100 | |
+----+-------------+-----------------------------+-------+---------------+---------+---------+------+------+-------+
1 row in set (0.00 sec)
Explain for query 2:
mysql> EXPLAIN select id, datetime_created from seventhcont_exceptionreport order by id LIMIT 100 OFFSET 7000;
+----+-------------+-----------------------------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------------------------+------+---------------+------+---------+------+------+----------------+
| 1 | SIMPLE | seventhcont_exceptionreport | ALL | NULL | NULL | NULL | NULL | 7067 | Using filesort |
+----+-------------+-----------------------------+------+---------------+------+---------+------+------+----------------+
1 row in set (0.00 sec)
UPDATE
Analyze table:
mysql> ANALYZE TABLE seventhcont_exceptionreport;
+--------------------------------+---------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+--------------------------------+---------+----------+----------+
| 7k.seventhcont_exceptionreport | analyze | status | OK |
+--------------------------------+---------+----------+----------+
1 row in set (2.51 sec)
I am no MySQL specialist, but I might be able to point you in the right direction.
In the first query, we can see in the Explain Plan, that an index access was used. In contrast, for the second query, we can see that a non-index access is performed (type index vs ALL). Also, we can see that MySQL is using Using filesort.
This means MySQL cannot perform the sort operation on the index and is therefore performing it on the data itself. This could be because the sort buffer is too small (also see https://www.percona.com/blog/2009/03/05/what-does-using-filesort-mean-in-mysql/).
Therefore, try to increase the size of your sort buffer (soft_buffer_size).
Is there a drawback to crate a unique key containing fields A, B, C and then create another one with fields C, B, A, so MySQL will take advantage of the index in case of searching only with A and only with C?
You don't want to create an additional composite UNIQUE constraint. The case when you access your data by only A is already covered by existing index (a, b, c). If you need to support queries with access path only by c then you can create an index just on c.
If your schema looks something like
mysql> create table tablex
-> (
-> a int not null,
-> b int not null,
-> c int not null
-> );
Query OK, 0 rows affected (0.03 sec)
mysql> insert into tablex values (1, 2, 3),(2, 3, 4),(1, 3, 3);
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> create unique index idx_abc_unique on tablex (a, b, c);
Query OK, 0 rows affected (0.06 sec)
Records: 0 Duplicates: 0 Warnings: 0
If you filter on A only you'll see that unique index is properly used because A is the leftmost prefix (keylen = 4) of the index. Extra column in EXPLAIN results shows Using index.
mysql> explain select * from tablex where a = 1;
+----+-------------+--------+------+----------------+----------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+----------------+---------+-------+------+-------------+
| 1 | SIMPLE | tablex | ref | idx_abc_unique | idx_abc_unique | 4 | const | 1 | Using index |
+----+-------------+--------+------+----------------+----------------+---------+-------+------+-------------+
1 row in set (0.00 sec)
Now if you try filter on C then you'll see a different story. EXPLAIN shows that MySQL is in fact using unique index but is doing full index scan (type = index) with a filter predicate identified by Using where in Extra column.
mysql> explain select * from tablex where c = 3;
+----+-------------+--------+-------+---------------+----------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+----------------+---------+------+------+--------------------------+
| 1 | SIMPLE | tablex | index | NULL | idx_abc_unique | 12 | NULL | 1 | Using where; Using index |
+----+-------------+--------+-------+---------------+----------------+---------+------+------+--------------------------+
1 row in set (0.00 sec)
Here is SQLFiddle demo
If we create an explicit index on C
mysql> create index idx_c on tablex (c);
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0
and take a look at EXPLAIN we'll see again Using index.
mysql> explain select * from tablex where c = 3;
+----+-------------+--------+------+---------------+-------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+-------+---------+-------+------+-------------+
| 1 | SIMPLE | tablex | ref | idx_c | idx_c | 4 | const | 1 | Using index |
+----+-------------+--------+------+---------------+-------+---------+-------+------+-------------+
1 row in set (0.00 sec)
Here is SQLFiddle demo
Lets say, I have the following mysql table :
CREATE TABLE player (
id int(9) unsigned NOT NULL DEFAULT 0 ,
score MEDIUMINT(8) unsigned NOT NULL DEFAULT 0,
signupdate DATE NOT NULL,
lastupdate DATE NOT NULL
) DEFAULT CHARACTER SET utf8 ENGINE=InnoDB;
Currently I have a primary key on id column. lastupdate column is updated everyday, and if its not updated it means that the player has deleted the account, this means the cardianlity of this column is very low.
Also there is a relational table matches with feilds matchid , playerid and matchdate
Most my queries are like
SELECT id,score,signupdate FROM player
JOIN matches ON matches.playerid = player.id
WHERE lastupdate = '{today}'
So 3 cases for indices come to my mind
PRIMARY KEY on id
PRIMARY KEY on id and an INDEX on lastupdate
PRIMARY KEY on (id,lastupdate)
Which one would be the best??
You should have an index on table matches column playerid and an index on table player column lastupdate.
As a very rough rule of thumb is that what you use in the WHERE and JOIN clause should have an index if it is a large table.
To get more information what index was used you can use the explain statement. Here is what it looks like. Notice the explain statement at the very end:
mysql> CREATE TABLE player (
-> id int(9) unsigned NOT NULL DEFAULT 0 ,
-> score MEDIUMINT(8) unsigned NOT NULL DEFAULT 0,
-> signupdate DATE NOT NULL,
-> lastupdate DATE NOT NULL
-> ) DEFAULT CHARACTER SET utf8 ENGINE=InnoDB;
Query OK, 0 rows affected (0.12 sec)
mysql>
mysql> CREATE TABLE matches (
-> matchid int(9) unsigned NOT NULL DEFAULT 0 ,
-> playerid int(9) unsigned NOT NULL DEFAULT 0 ,
-> matchdate DATE NOT NULL
-> ) DEFAULT CHARACTER SET utf8 ENGINE=InnoDB;
Query OK, 0 rows affected (0.22 sec)
mysql>
mysql> SELECT id,score,signupdate FROM player
-> JOIN matches ON matches.playerid = player.id
-> WHERE lastupdate = now()
-> ;
Empty set (0.00 sec)
mysql>
mysql> explain
-> SELECT id,score,signupdate FROM player
-> JOIN matches ON matches.playerid = player.id
-> WHERE lastupdate = '{today}'
-> ;
+----+-------------+---------+------+---------------+------+---------+------+------+--------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+------+--------------------------------+
| 1 | SIMPLE | player | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
| 1 | SIMPLE | matches | ALL | NULL | NULL | NULL | NULL | 1 | Using where; Using join buffer |
+----+-------------+---------+------+---------------+------+---------+------+------+--------------------------------+
2 rows in set, 2 warnings (0.00 sec)
mysql> CREATE INDEX player_idx_1
-> ON player (id)
-> ;
Query OK, 0 rows affected (0.15 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> CREATE INDEX matches_idx_1
-> ON matches (playerid)
-> ;
Query OK, 0 rows affected (0.16 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql>
mysql> explain SELECT id,score,signupdate FROM player JOIN matches ON matches.playerid = player.id WHERE lastupdate = '{today}';
+----+-------------+---------+------+---------------+---------------+---------+-----------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+---------------+---------+-----------------+------+-------------+
| 1 | SIMPLE | player | ALL | player_idx_1 | NULL | NULL | NULL | 1 | Using where |
| 1 | SIMPLE | matches | ref | matches_idx_1 | matches_idx_1 | 4 | mysql.player.id | 1 | Using index |
+----+-------------+---------+------+---------------+---------------+---------+-----------------+------+-------------+
2 rows in set, 2 warnings (0.00 sec)
mysql>
add the index for lastupdate
mysql> CREATE INDEX player_idx_2
-> ON player (lastupdate)
-> ;
Query OK, 0 rows affected (0.13 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> explain
-> SELECT id,score,signupdate FROM player
-> JOIN matches ON matches.playerid = player.id
-> WHERE lastupdate = curdate()
-> ;
+----+-------------+---------+------+---------------+---------------+---------+-----------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+---------------+---------+-----------------+------+-------------+
| 1 | SIMPLE | player | ref | player_idx_2 | player_idx_2 | 3 | const | 1 | |
| 1 | SIMPLE | matches | ref | matches_idx_1 | matches_idx_1 | 4 | mysql.player.id | 1 | Using index |
+----+-------------+---------+------+---------------+---------------+---------+-----------------+------+-------------+
2 rows in set (0.00 sec)
mysql>
Definitely number 2. Primary key is used to uniquely identify a row, and the id attribute is enough for that, so you don't need option 3. And since most of your queries look like what you said, then having an index on lastupdate will definitely be useful to speed your queries.
I need the SQL equivalent of this.
I have a table like this
ID MN MX
-- -- --
A 0 3
B 4 6
C 7 9
Given a number, say 5, I want to find the ID of the row where MN and MX contain that number, in this case that would be B.
Obviously,
SELECT ID FROM T WHERE ? BETWEEN MN AND MX
would do, but I have 9 million rows and I want this to run as fast as possible. In particular, I know that there can be only one matching row, I now that the MN-MX ranges cover the space completely, and so on. With all these constraints on the possible answers, there should be some optimizations I can make. Shouldn't there be?
All I have so far is indexing MN and using the following
SELECT ID FROM T WHERE ? BETWEEN MN AND MX ORDER BY MN LIMIT 1
but that is weak.
If you have an index spanning MN and MX it should be pretty fast, even with 9M rows.
alter table T add index mn_mx (mn, mx);
Edit
I just tried a test w/ a 1M row table
mysql> select count(*) from T;
+----------+
| count(*) |
+----------+
| 1000001 |
+----------+
1 row in set (0.17 sec)
mysql> show create table T\G
*************************** 1. row ***************************
Table: T
Create Table: CREATE TABLE `T` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`mn` int(10) DEFAULT NULL,
`mx` int(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `mn_mx` (`mn`,`mx`)
) ENGINE=InnoDB AUTO_INCREMENT=1048561 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
mysql> select * from T order by rand() limit 1;
+--------+-----------+-----------+
| id | mn | mx |
+--------+-----------+-----------+
| 112940 | 948004986 | 948004989 |
+--------+-----------+-----------+
1 row in set (0.65 sec)
mysql> explain select id from T where 948004987 between mn and mx;
+----+-------------+-------+-------+---------------+-------+---------+------+--------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+-------+---------+------+--------+--------------------------+
| 1 | SIMPLE | T | range | mn_mx | mn_mx | 5 | NULL | 239000 | Using where; Using index |
+----+-------------+-------+-------+---------------+-------+---------+------+--------+--------------------------+
1 row in set (0.00 sec)
mysql> select id from T where 948004987 between mn and mx;
+--------+
| id |
+--------+
| 112938 |
| 112939 |
| 112940 |
| 112941 |
+--------+
4 rows in set (0.03 sec)
In my example I just had an incrementing range of mn values and then set mx to +3 that so that's why I got more than 1, but should apply the same to you.
Edit 2
Reworking your query will definitely be better
mysql> explain select id from T where mn<=947892055 and mx>=947892055;
+----+-------------+-------+-------+---------------+-------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+-------+---------+------+------+--------------------------+
| 1 | SIMPLE | T | range | mn_mx | mn_mx | 5 | NULL | 9 | Using where; Using index |
+----+-------------+-------+-------+---------------+-------+---------+------+------+--------------------------+
It's worth noting even though the first explain reported many more rows to be scanned I had enough innodb buffer pool set to keep the entire thing in RAM after creating it; so it was still pretty fast.
If there are no gaps in your set, a simple gte comparison will work:
SELECT ID FROM T WHERE ? >= MN ORDER BY MN ASC LIMIT 1