| Revenue Source Veteran
Join Date: Oct 2005 Posts: 9,144 Jack of All Trades
CyberSpace
| Merge Tables Gotcha -
05-09-2007
I had the interesting customer case today which made me to do a bit research on the problem.
You can create merge table over merge tables which contain primary key and global uniqueness would not be enforced in this case, this is as far as most people will think about it. In fact however it is worse than that - if you have same key values in underlying merge tables some of the queries may give you wrong results: PLAIN TEXT
SQL: - [FONT='Courier New', Courier, monospace]mysql> CREATE TABLE t1(id int UNSIGNED PRIMARY KEY);[/font]
- [FONT='Courier New', Courier, monospace]Query OK, 0 rows affected (0.07 sec)[/font]
- [FONT='Courier New', Courier, monospace] [/font]
- [FONT='Courier New', Courier, monospace]mysql> CREATE TABLE t2(id int UNSIGNED PRIMARY KEY);[/font]
- [FONT='Courier New', Courier, monospace]Query OK, 0 rows affected (0.02 sec)[/font]
- [FONT='Courier New', Courier, monospace] [/font]
- [FONT='Courier New', Courier, monospace]mysql> CREATE TABLE tm(id int UNSIGNED NOT NULL PRIMARY KEY) type=merge union(t1,t2);[/font]
- [FONT='Courier New', Courier, monospace]Query OK, 0 rows affected, 1 warning (0.03 sec)[/font]
- [FONT='Courier New', Courier, monospace] [/font]
- [FONT='Courier New', Courier, monospace]mysql> INSERT INTO t1 VALUES(1);[/font]
- [FONT='Courier New', Courier, monospace]Query OK, 1 row affected (0.00 sec)[/font]
- [FONT='Courier New', Courier, monospace] [/font]
- [FONT='Courier New', Courier, monospace]mysql> INSERT INTO t2 VALUES(1);[/font]
- [FONT='Courier New', Courier, monospace]Query OK, 1 row affected (0.00 sec)[/font]
- [FONT='Courier New', Courier, monospace] [/font]
- [FONT='Courier New', Courier, monospace] [/font]
- [FONT='Courier New', Courier, monospace]mysql> SELECT * FROM tm WHERE id=1;[/font]
- [FONT='Courier New', Courier, monospace]+----+[/font]
- [FONT='Courier New', Courier, monospace]| id |[/font]
- [FONT='Courier New', Courier, monospace]+----+[/font]
- [FONT='Courier New', Courier, monospace]| 1 |[/font]
- [FONT='Courier New', Courier, monospace]+----+[/font]
- [FONT='Courier New', Courier, monospace]1 row IN SET (0.00 sec)[/font]
- [FONT='Courier New', Courier, monospace] [/font]
- [FONT='Courier New', Courier, monospace]mysql> SELECT * FROM tm;[/font]
- [FONT='Courier New', Courier, monospace]+----+[/font]
- [FONT='Courier New', Courier, monospace]| id |[/font]
- [FONT='Courier New', Courier, monospace]+----+[/font]
- [FONT='Courier New', Courier, monospace]| 1 |[/font]
- [FONT='Courier New', Courier, monospace]| 1 |[/font]
- [FONT='Courier New', Courier, monospace]+----+[/font]
- [FONT='Courier New', Courier, monospace]2 rows IN SET (0.00 sec)[/font]
- [FONT='Courier New', Courier, monospace] [/font]
- [FONT='Courier New', Courier, monospace]mysql> EXPLAIN SELECT * FROM tm WHERE id=1 \G[/font]
- [FONT='Courier New', Courier, monospace]*************************** 1. row ***************************[/font]
- [FONT='Courier New', Courier, monospace] id: 1[/font]
- [FONT='Courier New', Courier, monospace] select_type: SIMPLE[/font]
- [FONT='Courier New', Courier, monospace] TABLE: tm[/font]
- [FONT='Courier New', Courier, monospace] type: const[/font]
- [FONT='Courier New', Courier, monospace]possible_keys: PRIMARY[/font]
- [FONT='Courier New', Courier, monospace] KEY: PRIMARY[/font]
- [FONT='Courier New', Courier, monospace] key_len: 4[/font]
- [FONT='Courier New', Courier, monospace] ref: const[/font]
- [FONT='Courier New', Courier, monospace] rows: 1[/font]
- [FONT='Courier New', Courier, monospace] Extra: USING INDEX[/font]
- [FONT='Courier New', Courier, monospace]1 row IN SET (0.00 sec) [/font]
In this case there are obviously two rows with value 1 in merge table however select querying by primary index returns only one.
Following EXPLAIN shows why this is the case - MySQL knows this is primary key and pre-reads the value in question as "const" value. Similar things happens in JOIN queries when it would use "eq_ref" lookup type which means it will only do one lookup for each key value as it knows it is unique.
In the case I was working on it was required to enforce uniqueness in the tables which are later queries as merge table so we could not drop PRIMARY KEY so I thought I should try using key on the given column instead: PLAIN TEXT
SQL: - [FONT='Courier New', Courier, monospace]mysql> CREATE TABLE tx (id int UNSIGNED NOT NULL, KEY(id)) type=merge union(t1,t2);[/font]
- [FONT='Courier New', Courier, monospace]Query OK, 0 rows affected, 1 warning (0.02 sec)[/font]
- [FONT='Courier New', Courier, monospace] [/font]
- [FONT='Courier New', Courier, monospace]mysql> SELECT * FROM tx WHERE id=1;[/font]
- [FONT='Courier New', Courier, monospace]+----+[/font]
- [FONT='Courier New', Courier, monospace]| id |[/font]
- [FONT='Courier New', Courier, monospace]+----+[/font]
- [FONT='Courier New', Courier, monospace]| 1 |[/font]
- [FONT='Courier New', Courier, monospace]| 1 |[/font]
- [FONT='Courier New', Courier, monospace]+----+[/font]
- [FONT='Courier New', Courier, monospace]2 rows IN SET (0.00 sec)[/font]
- [FONT='Courier New', Courier, monospace] [/font]
- [FONT='Courier New', Courier, monospace] [/font]
- [FONT='Courier New', Courier, monospace]mysql> SELECT * FROM tx;[/font]
- [FONT='Courier New', Courier, monospace]+------+[/font]
- [FONT='Courier New', Courier, monospace]| id |[/font]
- [FONT='Courier New', Courier, monospace]+------+[/font]
- [FONT='Courier New', Courier, monospace]| 1 |[/font]
- [FONT='Courier New', Courier, monospace]| 1 |[/font]
- [FONT='Courier New', Courier, monospace]+------+[/font]
- [FONT='Courier New', Courier, monospace]2 rows IN SET (0.00 sec) [/font]
This way it works fine.
Interesting enough first time I tried it I made a mistake of not defining column as NOT NULL for the merge table:
"create table tx (id int unsigned, key(id)) type=merge union(t1,t2)" If you create table this way it structure will be in fact different from underlying tables as PRIMARY KEY on the column automatically changes it to NOT NULL (which I think is bad behavior and it would better throw the error and ask to change the column type instead).
There is in fact the note about converting primary key to the key in MySQL manual
Here is what it says: Note that the a column is indexed as a PRIMARY KEY in the underlying MyISAM tables, but not in the MERGE table. There it is indexed but not as a PRIMARY KEY because a MERGE table cannot enforce uniqueness over the set of underlying tables. This however only speaks about enforcing uniqueness it does not tell anything about wrong results you may get. Merge Tables Gotcha - Read More... |