Bug #17731 Joining with InnoDB tables yields invalid results
Submitted: 27 Feb 2006 1:10 Modified: 27 Feb 2006 9:35
Reporter: Mark Hughes Email Updates:
Status: Duplicate Impact on me:
None 
Category:MySQL Server Severity:S2 (Serious)
Version:mysql-standard-5.0.18 OS:Linux (RedHat Linux 9.0)
Assigned to: CPU Architecture:Any

[27 Feb 2006 1:10] Mark Hughes
Description:
Given the data in "How to repeat" the following query returns NULL for itemCity when we expect it to return "San Diego."

mysql> SELECT items.itemID, items.itemName, itemMetaData.itemCity FROM items LEFT JOIN itemMetaData ON itemMetaData.itemID = items.itemID INNER JOIN extractedItems ON extractedItems.siteID = 156 AND extractedItems.itemTypeID = 1 AND extractedItems.itemID = items.itemID WHERE extractedItems.itemID = 51;
+--------+-----------------------+----------+
| itemID | itemName              | itemCity |
+--------+-----------------------+----------+
|     51 | My super special item | NULL     |
+--------+-----------------------+----------+
1 row in set (0.00 sec)

I think this is isolated to InnoDB because if you change `extractedItems` to a MyISAM table, itemCity correctly returns "San Diego" each time.

Additionally, if you rebuild the InnoDB table after import:

  ALTER TABLE extractedItems ENGINE=InnoDB;

The query then DOES return the proper answer:

mysql> SELECT items.itemID, items.itemName, itemMetaData.itemCity FROM items LEFT JOIN itemMetaData ON itemMetaData.itemID = items.itemID INNER JOIN extractedItems ON extractedItems.siteID = 156 AND extractedItems.itemTypeID = 1 AND extractedItems.itemID = items.itemID WHERE extractedItems.itemID = 51;
+--------+-----------------------+-----------+
| itemID | itemName              | itemCity  |
+--------+-----------------------+-----------+
|     51 | My super special item | San Diego |
+--------+-----------------------+-----------+
1 row in set (0.00 sec)

--

Even further, if you remove the ENABLE/DISABLE KEYS, running the query before a table rebuild seems to work but then after a rebuild, the second one works only sometimes.

--

I'm not sure what's going on, but it sure seems like unexpected behaviour.

If you need more information, please let me know. Thanks!

  --  mjh

How to repeat:
-- MySQL dump 10.10
--
-- Host: localhost    Database: hg
-- ------------------------------------------------------
-- Server version	5.0.18-standard-log

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table `extractedItems`
--

DROP TABLE IF EXISTS `extractedItems`;
CREATE TABLE `extractedItems` (
  `siteID` mediumint(8) unsigned NOT NULL default '0',
  `itemTypeID` mediumint(8) unsigned NOT NULL default '0',
  `geoNodeID` mediumint(8) unsigned NOT NULL default '0',
  `itemID` mediumint(8) unsigned NOT NULL default '0',
  KEY `siteID` (`siteID`,`itemTypeID`,`geoNodeID`),
  KEY `siteID_2` (`siteID`,`itemTypeID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Dumping data for table `extractedItems`
--

/*!40000 ALTER TABLE `extractedItems` DISABLE KEYS */;
LOCK TABLES `extractedItems` WRITE;
INSERT INTO `extractedItems` VALUES (156,1,53097,51);
UNLOCK TABLES;
/*!40000 ALTER TABLE `extractedItems` ENABLE KEYS */;

--
-- Table structure for table `itemMetaData`
--

DROP TABLE IF EXISTS `itemMetaData`;
CREATE TABLE `itemMetaData` (
  `itemCity` varchar(255) NOT NULL default '',
  `itemID` mediumint(8) unsigned NOT NULL default '0',
  PRIMARY KEY  (`itemID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Dumping data for table `itemMetaData`
--

/*!40000 ALTER TABLE `itemMetaData` DISABLE KEYS */;
LOCK TABLES `itemMetaData` WRITE;
INSERT INTO `itemMetaData` VALUES ('San Diego',51);
UNLOCK TABLES;
/*!40000 ALTER TABLE `itemMetaData` ENABLE KEYS */;

--
-- Table structure for table `items`
--

DROP TABLE IF EXISTS `items`;
CREATE TABLE `items` (
  `itemName` varchar(255) NOT NULL default '',
  `itemID` mediumint(8) unsigned NOT NULL auto_increment,
  PRIMARY KEY  (`itemID`),
  UNIQUE KEY `itemName_2` (`itemName`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Dumping data for table `items`
--

/*!40000 ALTER TABLE `items` DISABLE KEYS */;
LOCK TABLES `items` WRITE;
INSERT INTO `items` VALUES ('My super special item',51);
UNLOCK TABLES;
/*!40000 ALTER TABLE `items` ENABLE KEYS */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

/*

	The query fails:

mysql> SELECT items.itemID, items.itemName, itemMetaData.itemCity FROM items LEFT JOIN itemMetaData ON itemMetaData.itemID = items.itemID INNER JOIN extractedItems ON extractedItems.siteID = 156 AND extractedItems.itemTypeID = 1 AND extractedItems.itemID = items.itemID WHERE extractedItems.itemID = 51;
+--------+-----------------------+----------+
| itemID | itemName              | itemCity |
+--------+-----------------------+----------+
|     51 | My super special item | NULL     |
+--------+-----------------------+----------+
1 row in set (0.00 sec)

*/

SELECT items.itemID, items.itemName, itemMetaData.itemCity FROM items LEFT JOIN itemMetaData ON itemMetaData.itemID = items.itemID INNER JOIN extractedItems ON extractedItems.siteID = 156 AND extractedItems.itemTypeID = 1 AND extractedItems.itemID = items.itemID WHERE extractedItems.itemID = 51;

/* ****  REBUILD THE TABLE **** */

ALTER TABLE extractedItems ENGINE=InnoDB;

/*

	The query works:

mysql> SELECT items.itemID, items.itemName, itemMetaData.itemCity FROM items LEFT JOIN itemMetaData ON itemMetaData.itemID = items.itemID INNER JOIN extractedItems ON extractedItems.siteID = 156 AND extractedItems.itemTypeID = 1 AND extractedItems.itemID = items.itemID WHERE extractedItems.itemID = 51;
+--------+-----------------------+-----------+
| itemID | itemName              | itemCity  |
+--------+-----------------------+-----------+
|     51 | My super special item | San Diego |
+--------+-----------------------+-----------+
1 row in set (0.00 sec)

*/

SELECT items.itemID, items.itemName, itemMetaData.itemCity FROM items LEFT JOIN itemMetaData ON itemMetaData.itemID = items.itemID INNER JOIN extractedItems ON extractedItems.siteID = 156 AND extractedItems.itemTypeID = 1 AND extractedItems.itemID = items.itemID WHERE extractedItems.itemID = 51;

---

/etc/my.cnf for server:

# Example mysql config file for large systems.
#
# This is for large system with memory = 512M where the system runs mainly
# MySQL.
#
# You can copy this file to
# /etc/my.cnf to set global options,
# mysql-data-dir/my.cnf to set server-specific options (in this
# installation this directory is /usr/local/mysql/var) or
# ~/.my.cnf to set user-specific options.
#
# One can in this file use all long options that the program supports.
# If you want to know which options a program support, run the program
# with --help option.

# The following options will be passed to all MySQL clients
[client]
#password       = your_password
port            = 3306
socket          = /tmp/mysql.sock
#old-passwords
# Here follows entries for some specific programs

# The MySQL server
[mysqld]
user            =       mysqld
port            = 3306
socket          = /tmp/mysql.sock
old-passwords
skip-locking
set-variable    = key_buffer=256M
set-variable    = max_allowed_packet=1M
set-variable    = table_cache=256
set-variable    = sort_buffer=1M
set-variable    = record_buffer=1M
set-variable    = myisam_sort_buffer_size=64M
set-variable    = thread_cache=8
# Try number of CPU's*2 for thread_concurrency
set-variable    = thread_concurrency=4
set-variable    = max_connections=500
sync-binlog=1
server-id      = 1  
#nice = -20

log-bin=binlog
relay-log=relaylog
log-error=errors.log

query_cache_type=1
query_cache_size=52428800

log-slow-queries
log-queries-not-using-indexes

# Uncomment the following if you are using BDB tables
#set-variable   = bdb_cache_size=64M
#set-variable   = bdb_max_lock=100000

# Uncomment the following if you are using InnoDB tables
innodb_data_home_dir = /db/mysql/data/
innodb_data_file_path = innodb:20M:autoextend
innodb_log_group_home_dir = /db/mysql/data/
innodb_log_arch_dir = /db/mysql/data/

# You can set .._buffer_pool_size up to 50 - 80 %
# of RAM but beware of setting memory usage too high
set-variable = innodb_buffer_pool_size=128M
set-variable = innodb_additional_mem_pool_size=20M
# Set .._log_file_size to 25 % of buffer pool size
set-variable = innodb_log_file_size=64M
set-variable = innodb_log_buffer_size=8M
innodb_flush_log_at_trx_commit=1
set-variable = innodb_lock_wait_timeout=50

# Point the following paths to different dedicated disks
#tmpdir         = /tmp/
#log-update     = /path-to-dedicated-directory/hostname

[mysqldump]
quick
set-variable    = max_allowed_packet=16M

[mysql]
#no-auto-rehash
# Remove the next comment character if you are not familiar with SQL
#safe-updates

[isamchk]
set-variable    = key_buffer=128M
set-variable    = sort_buffer=128M
set-variable    = read_buffer=2M
set-variable    = write_buffer=2M

[myisamchk]
set-variable    = key_buffer=128M
set-variable    = sort_buffer=128M
set-variable    = read_buffer=2M
set-variable    = write_buffer=2M
[mysqlhotcopy]
interactive-timeout

------------

uname -a for server:

Linux dev1.main.aresdirect.com 2.4.20-8 #1 Thu Mar 13 17:54:28 EST 2003 i686 i686 i386 GNU/Linux
[27 Feb 2006 9:35] MySQL Verification Team
This looks very much like bug 15633 (join order differences can affect optimizer and result in corrupted datasets) which is fixed.

Cannot repeat this bug on 5.0.19-bk or 5.1-bk. Btw, no need to rebuild the table, just re-execute the query and the result is correct on 2nd run.

mysql> SELECT items.itemID, items.itemName, itemMetaData.itemCity FROM
    -> items LEFT JOIN itemMetaData ON itemMetaData.itemID = items.itemID
    -> INNER JOIN extractedItems ON extractedItems.siteID = 156 AND
    -> extractedItems.itemTypeID = 1 AND
    -> extractedItems.itemID = items.itemID
    -> WHERE extractedItems.itemID = 51;
+--------+-----------------------+-----------+
| itemID | itemName              | itemCity  |
+--------+-----------------------+-----------+
|     51 | My super special item | San Diego |
+--------+-----------------------+-----------+
1 row in set (0.02 sec)

mysql> select version();
+--------------+
| version()    |
+--------------+
| 5.0.19-debug |
+--------------+
1 row in set (0.02 sec)