Search code examples
mysqlsqljoinquery-optimizationin-subquery

optimise IN subquery


I have a workers table and an associated workerGeofence table.

CREATE TABLE IF NOT EXISTS `workergeofences` (
`ID` int(11) NOT NULL,
  `WorkerID` varchar(20) NOT NULL,
  `GeofenceID` int(11) NOT NULL,
  `isActive` tinyint(4) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=107 DEFAULT CHARSET=latin1;

I need to return only workers who have at least one entry in the workerGeofences table with an isActive of 1.

I'm able to get the desired outcome withe following:

    SELECT distinct w.ID, Title, FName, SName, Email, Birthday, Address, Phone, description,
 companyID 
FROM Workers w WHERE companyID = ? 
and w.ID IN (SELECT WorkerID FROM WorkerGeofences WHERE isActive <> 0)
    limit ?,10

but the in subquery is exhaustive as when I run the explain, I can see it is scanning the entire table. How would I get around this?


Solution

  • First of all , your join is wrong! you are not comparing any common column on both table, you should add where workerGeofences.workerID = w.id like this:

    SELECT  w.ID, Title, FName, SName, Email, Birthday, Address, Phone,
            description, companyID
        FROM  Workers w
        join  workerGeofences
        WHERE  workerGeofences.workerID = w.ID companyID = ?
          and  w.ID IN (
            SELECT  WorkerID
                FROM  WorkerGeofences s
                WHERE  isActive <> 0
                  and  s.workerID = w.id
                       )
        limit  0,10 
    

    And second, you are not selecting anything from the second table, so the join is unessesary and in your IN statement, you are not comparing the right ID's so your query should be:

    SELECT  w.ID, Title, FName, SName, Email, Birthday, Address, Phone,
            description, companyID
        FROM  Workers w
        WHERE  companyID = ?
          and  w.ID IN (
            SELECT  WorkerID
                FROM  WorkerGeofences s
                WHERE  isActive <> 0
                  and  s.workerID = w.ID
                       )
        limit  0,10 
    

    Also, you can use EXISTS() for that.

    SELECT  w.ID, Title, FName, SName, Email, Birthday, Address, Phone,
            description, companyID
        FROM  Workers w
        WHERE  companyID = ?
          and  exists 
          ( SELECT  1
                FROM  WorkerGeofences s
                WHERE  isActive = 1
                  and  s.workerID = w.ID
          )
        limit  0,10