Search code examples
mysqlinsertlockingon-duplicate-key

How to insert or update all values in FIND_IN_SET and update the NOT FIND_IN_SET On Duplicate Key update


here is what im trying to do:

Table called departments has many-to-many relation with table called employees. so, table department_employees has columns: department_id, employee_id, is_active.

indexed department_id, employee_id as pk.

I get a string of employees ids like '12,15,18,19'. I get the department id.

I call a Mysql stored procedure (routine) that should:

  • insert all the employees to the department
  • if they exist and NOT active than activate
  • all the other employees should be deactivated

CREATE PROCEDURE (IN @dep_id INT, IN @emp_ids TEXT)

INSERT INTO TBL_DEPARTMENT_EMPLOYEES (DEPARTMENT_ID, EMPLOYEE_ID, IS_ACTIVE) SELECT @dep_id, EMPLOYEE_ID, 1 FROM TBL_EMPLOYEES WHERE FIND_IN_SET(EMPLOYEE_ID, @emp_ids) ON DUPLICATE KEY UPDATE IS_ACTIVE=1;

UPDATE TBL_DEPARTMENT_EMPLOYEES SET IS_ACTIVE=0 WHERE DEPARTMENT_ID=@dep_id AND NOT FIND_IN_SET(EMPLOYEE_ID, @emp_ids);

only the first query runs and i guess the second one cant because of locking or something, i tried the TRANSACTION - COMMIT trick, didnt work. I thought maybe one of the awesome guys here can help or eben make it all run in a single query. thanks!


Solution

  • This procedure is not pretty, but its the best I can come up with given the concatenated IDs. It should also work faster than the FIND_IN_SET solution, because it can use the index on EMPLOYEE_ID.

    CREATE PROCEDURE `test`(IN `dep_id` INT, IN `emp_ids` TEXT)
        LANGUAGE SQL
        NOT DETERMINISTIC
        MODIFIES SQL DATA
        SQL SECURITY DEFINER
        COMMENT ''
    BEGIN
    START TRANSACTION;
    IF(emp_ids REGEXP '^[0-9,]+$') THEN #this will NOT guarantee a valid query but will prevent injections
        UPDATE TBL_DEPARTMENT_EMPLOYEES SET IS_ACTIVE=0 WHERE DEPARTMENT_ID=dep_id;
        SET @q = CONCAT('
            INSERT INTO TBL_DEPARTMENT_EMPLOYEES (DEPARTMENT_ID, EMPLOYEE_ID, IS_ACTIVE) 
            SELECT ?, EMPLOYEE_ID, 1 
            FROM TBL_EMPLOYEES 
            WHERE EMPLOYEE_ID IN (',emp_ids,')
            ON DUPLICATE KEY UPDATE IS_ACTIVE=1;
        '); 
        PREPARE stmt1 FROM @q;
        SET @dep_id = dep_id;
        EXECUTE stmt1 USING @dep_id;
        DEALLOCATE PREPARE stmt1;
    END IF;
    COMMIT;
    END ;