Bug #63686 Nested LEFT JOIN returns invalid results
Submitted: 8 Dec 2011 21:58 Modified: 9 Jan 2012 5:00
Reporter: Andrew Jones Email Updates:
Status: No Feedback Impact on me:
None 
Category:MySQL Server: DML Severity:S2 (Serious)
Version:5.0.45-community OS:Linux (Red Hat)
Assigned to: CPU Architecture:Any
Tags: left join, missing data

[8 Dec 2011 21:58] Andrew Jones
Description:
I have a query that is missing results when I add a LEFT JOIN.  It only happens on certain LINUX machines.  We haven't found a pattern yet.
No errors were thrown, it just returns incorrect results on certain LINUX installs.

LINUX version 2.6.31.6-166.fc12.x86_64 (mockbuild@x86-1.fedora.phx.redhat.com) (gcc version 4.4.2 20091027 (Red Hat 4.4.2-7) (GCC) )

How to repeat:
I use this query:

-- Q1:

SELECT count(*)
FROM posCustomer
JOIN acrCustomer
ON posCustomer.customerId = acrCustomer.customerId
AND ( acrCustomer.securityPerms IS NULL OR acrCustomer.securityPerms NOT LIKE '%[01-1=N]%')
JOIN acrCustomer masterCustomer
ON masterCustomer.customerNumber = acrCustomer.customerNumber
AND masterCustomer.entityId = acrCustomer.entityId
AND masterCustomer.customerSubacct = '00000'
WHERE posCustomer.customerStatus = 'A'
AND posCustomer.entityId = '01'
AND posCustomer.customerSubacct = '00000'
AND posCustomer.recordType NOT IN ( 'INTSTORE', 'INHOUSE' )
AND posCustomer.entityId = '01'
AND ( masterCustomer.securityPerms IS NULL OR masterCustomer.securityPerms NOT LIKE '%[01-1=N]%' )

and it returns a few hundred results.  (I'm doing a count(*) for this example.  The actual query I use specifies columns from each of the tables.)
When I add a nested LEFT JOIN:

-- Q2:

SELECT count(*)
FROM posCustomer
JOIN acrCustomer
ON posCustomer.customerId = acrCustomer.customerId
AND ( acrCustomer.securityPerms IS NULL OR acrCustomer.securityPerms NOT LIKE '%[01-1=N]%')
JOIN acrCustomer masterCustomer
ON masterCustomer.customerNumber = acrCustomer.customerNumber
AND masterCustomer.entityId = acrCustomer.entityId
AND masterCustomer.customerSubacct = '00000'
LEFT JOIN ( 
  acrCustAddress primaryAddress
  JOIN acrCustContact primaryContact
  ON primaryContact.addressId = primaryAddress.addressId
  AND primaryContact.primaryContact = 'Y'
  AND primaryContact.startDate <= DATE_FORMAT(CURDATE(),'%Y/%m/%d')
  AND ( primaryContact.endDate IS NULL OR primaryContact.endDate >= DATE_FORMAT(CURDATE(),'%Y/%m/%d') )

  LEFT JOIN sysState primaryCity
  ON primaryCity.stateCityId = primaryAddress.cityId

  LEFT JOIN sysState primaryState
  ON primaryState.stateCityId = primaryAddress.stateId

  LEFT JOIN acrCustContactMethod primaryPhone
  ON primaryPhone.addressId = primaryContact.addressId
  AND primaryPhone.contactUniqueId = primaryContact.uniqueId
  AND primaryPhone.contactMethod = 'P'
  AND primaryPhone.defaultMethod = 'Y'

  LEFT JOIN acrCustContactMethod primaryFax
  ON primaryFax.addressId = primaryContact.addressId
  AND primaryFax.contactUniqueId = primaryContact.uniqueId
  AND primaryFax.contactMethod = 'F'
  AND primaryFax.defaultMethod = 'Y'

  LEFT JOIN acrCustContactMethod primaryEmail
  ON primaryEmail.addressId = primaryContact.addressId
  AND primaryEmail.contactUniqueId = primaryContact.uniqueId
  AND primaryEmail.contactMethod = 'L'
  AND primaryEmail.defaultMethod = 'Y'
)
ON primaryAddress.customerId = acrCustomer.customerId
AND primaryAddress.startDate <= DATE_FORMAT(CURDATE(),'%Y/%m/%d')
AND ( primaryAddress.endDate IS NULL OR primaryAddress.endDate >= DATE_FORMAT(CURDATE(),'%Y/%m/%d') )
WHERE posCustomer.customerStatus = 'A'
AND posCustomer.entityId = '01'
AND posCustomer.customerSubacct = '00000'
AND posCustomer.recordType NOT IN ( 'INTSTORE', 'INHOUSE' )
AND posCustomer.entityId = '01'
AND ( masterCustomer.securityPerms IS NULL OR masterCustomer.securityPerms NOT LIKE '%[01-1=N]%' )

my results are limited to 7.  Those 7 are the 7 records in acrCustContact aliased as primaryContact. There should be a few hundred results since I'm only adding on a LEFT JOIN.   But for some reason, the JOIN to acrCustContact aliased primaryContact is limiting my results.  

Oddly, I can get my hundreds of results by doing one of the following:
 * Change the join to primaryContact to a LEFT JOIN
 * Remove any one of the LEFT JOINs in the nested LEFT JOIN (such as sysState which is aliased as primaryCity)

Suggested fix:
This is how I "fixed" it.  I turned my nested LEFT JOIN into a subquery and that gave me the correct results that I expected and usually get from Q2. (In this example for Q3, I use "SELECT *", but in reality I of course specified columns)

-- Q3

SELECT COUNT(*)
FROM posCustomer
JOIN acrCustomer
ON posCustomer.customerId = acrCustomer.customerId
AND ( acrCustomer.securityPerms IS NULL OR acrCustomer.securityPerms NOT LIKE '%[01-1=N]%')
JOIN acrCustomer masterCustomer
ON masterCustomer.customerNumber = acrCustomer.customerNumber
AND masterCustomer.entityId = acrCustomer.entityId
AND masterCustomer.customerSubacct = '00000'
LEFT JOIN ( 
  Select *
  From acrCustAddress primaryAddress
  JOIN acrCustContact primaryContact
  ON primaryContact.addressId = primaryAddress.addressId
  AND primaryContact.primaryContact = 'Y'
  AND primaryContact.startDate <= DATE_FORMAT(CURDATE(),'%Y/%m/%d')
  AND ( primaryContact.endDate IS NULL OR primaryContact.endDate >= DATE_FORMAT(CURDATE(),'%Y/%m/%d') )

  LEFT JOIN sysState primaryCity
  ON primaryCity.stateCityId = primaryAddress.cityId

  LEFT JOIN sysState primaryState
  ON primaryState.stateCityId = primaryAddress.stateId

  LEFT JOIN acrCustContactMethod primaryPhone
  ON primaryPhone.addressId = primaryContact.addressId
  AND primaryPhone.contactUniqueId = primaryContact.uniqueId
  AND primaryPhone.contactMethod = 'P'
  AND primaryPhone.defaultMethod = 'Y'

  LEFT JOIN acrCustContactMethod primaryFax
  ON primaryFax.addressId = primaryContact.addressId
  AND primaryFax.contactUniqueId = primaryContact.uniqueId
  AND primaryFax.contactMethod = 'F'
  AND primaryFax.defaultMethod = 'Y'

  LEFT JOIN acrCustContactMethod primaryEmail
  ON primaryEmail.addressId = primaryContact.addressId
  AND primaryEmail.contactUniqueId = primaryContact.uniqueId
  AND primaryEmail.contactMethod = 'L'
  AND primaryEmail.defaultMethod = 'Y'
) as query1
ON query1.customerId = acrCustomer.customerId
AND query1.startDate <= DATE_FORMAT(CURDATE(),'%Y/%m/%d')
AND ( query1.endDate IS NULL OR query1.endDate >= DATE_FORMAT(CURDATE(),'%Y/%m/%d') )
WHERE posCustomer.customerStatus = 'A'
AND posCustomer.entityId = '01'
AND posCustomer.customerSubacct = '00000'
AND posCustomer.recordType NOT IN ( 'INTSTORE', 'INHOUSE' )
AND posCustomer.entityId = '01'
AND ( masterCustomer.securityPerms IS NULL OR masterCustomer.securityPerms NOT LIKE '%[01-1=N]%' )
[9 Dec 2011 5:00] Valeriy Kravchuk
Please, check if the problem is repeatable with newer versions of MySQL server, 5.0.91+, or, even better, 5.1.60. 5.0.x is going out of active support at the end of this year.
[9 Jan 2012 7:00] Bugs System
No feedback was provided for this bug for over a month, so it is
being suspended automatically. If you are able to provide the
information that was originally requested, please do so and change
the status of the bug back to "Open".