MySQL multiple unique keys, drawback - mysql

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

Related

How to uniquely order a MySQL table with a multi column primary key

Good day all
I have a strange query.
Let's say I have a table with a composite primary key (2 columns).
CREATE TABLE `testtable` (
`ifk1` INT(10) NOT NULL,
`ifk2` INT(10) NOT NULL,
`data1` VARCHAR(10) DEFAULT NULL,
PRIMARY KEY (`ifk1`,`ifk2`),
UNIQUE KEY `keyName` (`data1`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4
Let's add some basic data
INSERT INTO testtable(ifk1 , ifk2 , data1)
VALUES (1 , 2 , 'a') , (5 , 2 , 'b') , (2 , 4 , 'c') , (5 , 8 , 'd') , (2 , 2 , 'e') , (2 , 5 , 'f');
Let's do a simple SELECT to see what order the data comes out in:
ifk1 ifk2 data1
1 2 a
2 2 e
2 4 c
2 5 f
5 2 b
5 8 d
Now, what if I want to write some code to iterate through the table, grabbing X number of records at a time.
With a small set of data, this is simple:
SELECT * FROM testtable LIMIT 0 , 2;
SELECT * FROM testtable LIMIT 2 , 2;
SELECT * FROM testtable LIMIT 4 , 2;
This is going to run into some problems as the table gets bigger, as it's not using a WHERE clause and so not using an INDEX.
How do I use a WHERE clause to replicate the above SELECTS?
SELECT * FROM testtable WHERE ifk1 > 0 AND ifk2 > 0 LIMIT 2; -- this will work
The first one is easy, but what about the others?
Is there a way to do that?
A LIMIT clause without an ORDER BY clause is arbitrary. All three queries you are showing:
SELECT * FROM testtable LIMIT 0 , 2;
SELECT * FROM testtable LIMIT 2 , 2;
SELECT * FROM testtable LIMIT 4 , 2;
could return the exact same two rows. So, you must add an ORDER BY clause to make this work reliably: ORDER BY ifk1, ifk2.
But, yes, having to sort the data again and again for every access can take a lot of time. This is why we try to avoid using offsets and work with a key instead:
SELECT *
FROM testtable
WHERE ifk1 > #last_ifk1 OR (ifk1 = #last_ifk1 AND ifk2 > #last_ifk2)
ORDER BY ifk1, ifk2
LIMIT 2;
Paging is almost always quite slow. But this access method can use the primary key's unique index on (ifk1, ifk2) and access the next two rows very quickly. It depends on the implemantation in MySQL and its version how fast this is.
I am not sure I understand the index part of your question.
But generally, if you want to iterate over a bigger result set you can use a cursor as described here:
https://www.mysqltutorial.org/mysql-cursor/
This would be for a stored procedure but db drivers for other languages will expose similar functionality.
If your request is to use indexes: Logic is the same:
mysql> SELECT * FROM testtable WHERE ifk1 > 0 AND ifk2 > 0 LIMIT 2,2;
+------+------+-------+
| ifk1 | ifk2 | data1 |
+------+------+-------+
| 2 | 4 | c |
| 5 | 8 | d |
+------+------+-------+
2 rows in set (0.00 sec)
mysql> EXPLAIN SELECT * FROM testtable WHERE ifk1 > 0 AND ifk2 > 0 LIMIT 2,2;
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | testtable | NULL | index | PRIMARY | keyName | 43 | NULL | 6 | 33.33 | Using where; Using index |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> SELECT * FROM testtable WHERE ifk1 > 0 AND ifk2 > 0 LIMIT 4,2;
+------+------+-------+
| ifk1 | ifk2 | data1 |
+------+------+-------+
| 2 | 2 | e |
| 2 | 5 | f |
+------+------+-------+
2 rows in set (0.00 sec)
mysql> EXPLAIN SELECT * FROM testtable WHERE ifk1 > 0 AND ifk2 > 0 LIMIT 4,2;
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | testtable | NULL | index | PRIMARY | keyName | 43 | NULL | 6 | 33.33 | Using where; Using index |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
Above uses the KeyName indexes: Maybe you should deactivate or drop KeyName index, and enable the query to use PK composite indexes: Please follow these steps to achieve it:
DROP keyName index first:
mysql> ALTER TABLE testtable
-> DROP INDEX keyName;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
the run the query again to see that PK Composite keys are used in the query which I think make the query faster:
mysql> EXPLAIN SELECT * FROM testtable WHERE ifk1 > 0 AND ifk2 > 0 LIMIT 2,2;
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | SIMPLE | testtable | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 6 | 33.33 | Using where |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

How does MySQL index not speed up update query?

I have a table located in RAM and doing some performance tests.
Let's consider a sample query, adding explain sentences along with results
mysql> explain update users_ram set balance = balance + speed where sub = 1;
+----+-------------+-----------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| 1 | UPDATE | users_ram | NULL | ALL | NULL | NULL | NULL | NULL | 2333333 | 100.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set (0.00 sec)
mysql> update users_ram set balance = balance + speed where sub = 1;
Query OK, 1166970 rows affected (0.37 sec)
Rows matched: 1166970 Changed: 1166970 Warnings: 0
As you can see, it takes 0.37 sec without index. Then I'm creating an index on the sub column, which is an int column with just two possible values of 0 and 1, and surprisingly nothing changes
mysql> create index sub on users_ram (sub);
Query OK, 2333333 rows affected (2.04 sec)
Records: 2333333 Duplicates: 0 Warnings: 0
mysql> show index from lords.users_ram;
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| users_ram | 0 | user | 1 | user | NULL | 2333333 | NULL | NULL | YES | HASH | | |
| users_ram | 1 | sub | 1 | sub | NULL | 2 | NULL | NULL | | HASH | | |
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.00 sec)
mysql> explain update users_ram set balance = balance + speed where sub = 1;
+----+-------------+-----------+------------+-------+---------------+------+---------+-------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+------+---------+-------+---------+----------+-------------+
| 1 | UPDATE | users_ram | NULL | range | sub | sub | 5 | const | 1166666 | 100.00 | Using where |
+----+-------------+-----------+------------+-------+---------------+------+---------+-------+---------+----------+-------------+
1 row in set (0.00 sec)
mysql> update users_ram set balance = balance + speed where sub = 1;
Query OK, 1166970 rows affected (0.37 sec)
Rows matched: 1166970 Changed: 1166970 Warnings: 0
If I remove the index and add it again, but now using btree, it gets even more weird
mysql> explain update users_ram set balance = balance + speed where sub = 1;
+----+-------------+-----------+------------+-------+---------------+------+---------+-------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+------+---------+-------+---------+----------+-------------+
| 1 | UPDATE | users_ram | NULL | range | sub | sub | 5 | const | 1057987 | 100.00 | Using where |
+----+-------------+-----------+------------+-------+---------------+------+---------+-------+---------+----------+-------------+
1 row in set (0.00 sec)
mysql> update users_ram set balance = balance + speed where sub = 1;
Query OK, 1166970 rows affected (0.62 sec)
Rows matched: 1166970 Changed: 1166970 Warnings: 0
How could adding an index could have no effect or even slow down the query?
Let's take into account that I'm not modifying the column which is indexed, so mysql doesn't have to do an extra write operation, so really I can't get what's really happening here.
"table located in RAM" -- I suspect that is technically incorrect. The possibilities (in MySQL):
The table lives on disk, but it is usually fully cached in the in-RAM "buffer_pool".
The table is ENGINE=MEMORY. But that is used only for temp stuff; it is completely lost if the server goes down.
update users_ram set balance = balance + speed where sub = 1;
The table users_ram needs some index starting with sub. With such, it can go directly to the row(s). But...
It seems that there are 1166970 such rows. That seems like half the table?? At which point, the index is pretty useless. But...
Updating 1M rows is terribly slow, regardless of indexing.
Plan A: Avoid the UPDATE. Perhaps this can be done by storing speed in some other table and doing the + whenever you read the data. (It is generally bad schema design to need huge updates like that.)
Plan B: Update in chunks: http://mysql.rjweb.org/doc.php/deletebig#deleting_in_chunks
How the heck did you get index-type to be HASH? Perhaps `ENGINE=MEMORY? What version of MySQL?
What is speed? Another column? A constant?
Please provide SHOW CREATE TABLE users_ram -- There are some other things we need to see, such as the PRIMARY KEY and ENGINE.
(I need some of the above info before tackling "How could adding an index could have no effect or even slow down the query?")

Multiple column INDEX and single column INDEX SQL/PHP

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.

two indices in one table, is second index which is used only in where clause worth it?

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.

Faster to do a 'WHERE IN (SELECT)' or 'WHERE x = (SELECT)'

Which select statement is better?
SELECT *
FROM aTable
WHERE aField in (
SELECT xField
FROM bTable
WHERE yField > 5
);
OR
SELECT *
FROM aTable
WHERE (
SELECT yField
FROM bTable
WHERE aTable.aField = bTable.xField
) > 5;
They produce very similar execution plans (on my test tables, which are tiny; YMMV, always profile real data), and there's a third alternative you may want to consider instead:
The first:
EXPLAIN SELECT * FROM aTable WHERE aField in (SELECT xField FROM bTable WHERE yField > 5);
+----+--------------------+--------+-------+---------------+---------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+--------+-------+---------------+---------------+---------+------+------+-------------+
| 1 | PRIMARY | aTable | ALL | NULL | NULL | NULL | NULL | 4 | Using where |
| 2 | DEPENDENT SUBQUERY | bTable | range | bTable_yField | bTable_yField | 5 | NULL | 2 | Using where |
+----+--------------------+--------+-------+---------------+---------------+---------+------+------+-------------+
The second:
EXPLAIN SELECT * FROM aTable WHERE (SELECT yField FROM bTable WHERE aTable.aField = bTable.xField) > 5;
+----+--------------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+--------+------+---------------+------+---------+------+------+-------------+
| 1 | PRIMARY | aTable | ALL | NULL | NULL | NULL | NULL | 4 | Using where |
| 2 | DEPENDENT SUBQUERY | bTable | ALL | NULL | NULL | NULL | NULL | 4 | Using where |
+----+--------------------+--------+------+---------------+------+---------+------+------+-------------+
Both result in a dependent subquery; on my example tables, the first one gets the benefit of the index (I assume bTable.yField is indexed) while the second doesn't.
You can avoid the dependent subquery and get better up-front filtering using a JOIN:
The third alternative:
EXPLAIN SELECT * FROM aTable INNER JOIN bTable On aTable.aField = bTable.xField WHERE bTable.yField > 5;
+----+-------------+--------+-------+---------------+---------------+---------+------+------+--------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+---------------+---------+------+------+--------------------------------+
| 1 | SIMPLE | bTable | range | bTable_yField | bTable_yField | 5 | NULL | 2 | Using where |
| 1 | SIMPLE | aTable | ALL | NULL | NULL | NULL | NULL | 4 | Using where; Using join buffer |
+----+-------------+--------+-------+---------------+---------------+---------+------+------+--------------------------------+
Again, though, you really have to profile with your schema and your representative real-world data, as the optimizer may make different decisions.
More comparing these sorts of techniques in this excellent article by quassnoi.
For reference, here is how I created aTable and bTable (as you didn't provide definitions) and tested your queries:
mysql> CREATE TABLE aTable (aField INT, aMore VARCHAR(200));
Query OK, 0 rows affected (0.01 sec)
mysql> CREATE TABLE bTable (xField INT, yField INT);
Query OK, 0 rows affected (0.02 sec)
mysql> INSERT INTO aTable (aField, aMore) VALUES (1, 'One'), (2, 'Two'), (3, 'Three'), (4, 'Four');
Query OK, 4 rows affected (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> INSERT INTO bTable (xField, yField) VALUES (1, 10), (2, 2), (3, 20), (4, 4);
Query OK, 4 rows affected (0.02 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> CREATE INDEX bTable_yField ON bTable(yField);
Query OK, 0 rows affected (0.05 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM aTable WHERE aField in (SELECT xField FROM bTable WHERE yField > 5);
+--------+-------+
| aField | aMore |
+--------+-------+
| 1 | One |
| 3 | Three |
+--------+-------+
2 rows in set (0.00 sec)
mysql> SELECT * FROM aTable WHERE (SELECT yField FROM bTable WHERE aTable.aField = bTable.xField) > 5;
+--------+-------+
| aField | aMore |
+--------+-------+
| 1 | One |
| 3 | Three |
+--------+-------+
2 rows in set (0.00 sec)
I think the second one translates to correlated sub-query semantics and so is costly, compared to the first one. The best would be to just JOIN the two tables, as follows:
SELECT
a.*
FROM
aTable a
JOIN bTable b
ON aTable.aField = bTable.xField
WHERE
b.xField > 5
This will save you from large number of results in the IN clause, in case of the first query, that would make the query execution slower, and at times results in overflow error (SQL Server used to have a limit of 32767 values in the IN clause after which it used to throw this overflow error).
Alot depends on the indexing of the tables and whether indexed columns are used within the join condition. A combination of these will go some way as to how the SQL Engine 'decides' to construct the query internally and will ultimately affect the query performance. Not too sure on MySQL but certainly SQL Server will allow an Execution Plan to be created which will show potential bottlenecks.