简体   繁体   中英

MySQL, NOT IN rewrite to LEFT JOIN

As MySQL has very bad performance for NOT IN query. So I'm trying to rewrite this query by using LEFT JOIN but it seems that my query results are inconsistent. Not too sure where did i go wrong with this.

Here is my MySQL query:

SELECT DISTINCT '' AS ID,
                ADNO,
                PDATE,
                REMARKS
FROM AOE_tbl
WHERE ADNO NOT IN
    (SELECT adno.meta_value AS 'ADNO'
     FROM wp_postmeta AS adno
     WHERE adno.meta_key = 'adno')
  AND ADNO NOT IN
    (SELECT ADNO
     FROM AOE_tbl
     WHERE REMARKS IN ('private', 'trash')
       OR STATUS = 'VOIDED')
GROUP BY ADNO;

which I rewrite to:

SELECT DISTINCT '' AS ID,
                AOE1.ADNO,
                AOE1.PDATE,
                AOE1.REMARKS
FROM AOE_tbl AOE1
LEFT JOIN wp_postmeta AS adno ON AOE1.ADNO = adno.meta_value
AND adno.meta_key = 'adno'
LEFT JOIN AOE_tbl AS AOE2 ON AOE1.ADNO = AOE2.ADNO
AND AOE2.REMARKS IN ('private', 'trash')
OR AOE2.STATUS = 'VOIDED'
WHERE adno.meta_key IS NULL
  AND AOE2.REMARKS IS NULL
  AND AOE2.STATUS IS NULL
GROUP BY AOE1.ADNO;

I need help to see if there's anything I missed out in above query.
Thanks in advance.

AND has a higher order of precedence than OR .

The problem is in the join condition for AEO2 . Notice that the addition of the parens will cause the OR operation to be evaluated before the AND operator.

       ... AOE2
    on AOE2.ADNO = AOE1.ADNO
   AND (  AOE2.REMARKS IN ('private', 'trash')
       OR AOE2.STATUS = 'VOIDED'
       )

Without the added parens, the AND operator is evaluated first, then then the OR condition... which means that query is checking for any rows in the table with status 'VOIDED' , and not just rows with a matching ADNO .

The above is equivalent to:

       ... AOE2
    ON (     AOE2.ADNO = AOE1.ADNO
         AND AOE2.REMARKS IN ('private', 'trash')
       )
    OR (   AOE2.ADNO = AOE1.ADNO
       AND AOE2.STATUS = 'VOIDED'
       )

With this form, the parens are redundant. If we remove them, it would be the same. (The AND operations evaluated before the OR operation.

Also, the anti-join condition in the WHERE clause could be simplified. We're guaranteed that AEO2.ADNO will not be NULL if any matching rows are found, which means AEO2.ADNO will be NULL only if no matching rows are found.

Personally, I would avoid the OR condition. With appropriate indexes available, it's likely that a third anti-join would perform better.

       ... AOE2
    ON AOE2.ADNO = AOE1.ADNO
   AND AOE2.REMARKS IN ('private', 'trash')

  LEFT
  JOIN AOE_tbl AEO3
    ON AOE3.ADNO = AOE1.ADNO
   AND AOE3.STATUS = 'VOIDED'

WHERE adno.meta_key IS NULL
  AND AOE2.ADNO IS NULL
  AND AOE3.ADNO IS NULL

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM