MySQL Prepared Statement - How to Loop Through

后端 未结 2 664
轻奢々
轻奢々 2021-01-25 05:38

I have a table filled with ID numbers, which I need to loop through, and use as variables in a prepared statement. I do not know if I need to use a stored procedure for this, o

相关标签:
2条回答
  • 2021-01-25 05:59

    As others have already suggested, we typically avoid looping through a resultset RBAR (row by agonizing row) primarily for performance reasons. We just don't want to get into the habit of looping through a resultset. But that doesn't answer the question you asked.

    To answer the question you asked, here's a rudimentary example of a MySQL stored program that uses a CURSOR to individually process rows returned by a query. MySQL doesn't support anonymous blocks, so the only way to do this is in a MySQL stored program, like a PROCEDURE

    DELIMITER $$
    
    CREATE PROCEDURE loop_through_var_list
    BEGIN
       DECLARE done INT DEFAULT 0;
       DECLARE v_id INT DEFAULT NULL;  
       DECLARE csr_var_list CURSOR FOR SELECT id FROM var_list ORDER BY id;
       DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
       OPEN csr_var_list;
       get_id: LOOP
          FETCH csr_var_list INTO v_id; 
          IF done = 1 THEN
             LEAVE get_id;
          END IF;
    
          -- at this point, we have an id value in v_id, so we can do whatever
          SET @s1 = CONCAT('SELECT ... WHERE id =''', v_id, ''' ...');
    
    
       END LOOP get_id;
       CLOSE csr_var_list;
    END$$
    
    DELIMITER ;
    

    To execute the procedure:

    CALL loop_through_var_list();
    

    NOTES: The syntax for processing a CURSOR in MySQL is quite a bit different than other databases.

    To get the "looping", we need to use a LOOP ... END LOOP construct.

    But to prevent that loop from running forever, we need a LEAVE statement that will allow us to exit the loop.

    We use a conditional test to determine when to leave. In this example, we want to exit after we've finished processing the last row.

    The FETCH is going to throw an exception when there are no more rows to be fetched.

    We "catch" that exception in a CONTINUE HANDLER (for some arcane reason, the "handlers" have to the last things declared; MySQL throws an error if we try to declare something after a HANDLER (other than another HANDLER.)

    When MySQL throws the "no more rows" exception, that fires the handler code. In this example, we're just setting a variable (named done) to a value.

    Since it's a "continue" handler, processing starts back up at the statement where the exception was thrown, in this case, that will be the statement following the FETCH. So, the first thing we do is check if we're "done" or not. If we're "done", then we exit the loop, and close the cursor.

    Otherwise, we know that we've got an id value from var_list stored in a procedure variable named v_id. So now we can do whatever we want. Looks like you want to put some SQL text into a user-defined variable (including in the value of v_id into the SQL text, then PREPARE, EXECUTE, and DEALLOCATE PREPARE.

    Be sure to declare the v_id variable with the appropriate datatype, that matches the datatype of the id column in var_list, I've just assumed that it's an INT.

    When we reach the end of the loop, MySQL "loops" back to the beginning of the loop, and off we go again.

    In the body of the loop, likely you'll want to CONCAT v_id into the SQL text you want to execute. Looks like you already have a handle on the PREPARE, DEALLOCATE prepare. For testing, you may want to add a LIMIT clause on the SELECT in the cursor declaration, and then do a simple SELECT v_id; in the body, just to verify the loop is working, before you add more code.


    FOLLOWUP

    I wanted to mention another alternative approach to the task, i.e. running a series of statements based on a template, substituting in values provided from a single SQL select statement...

    For example, if I had this template:

    SELECT * 
      INTO OUTFILE '/tmp/orders_@ID.csv'
      FIELDS TERMINATED BY ',' ENCLOSED BY '"'
      LINES TERMINATED BY '\n'
    FROM data 
    WHERE id = @ID
    ORDER BY 1 
    

    and I needed to replace the occurrences of @ID with a specific id value from a list returned from a SELECT statement, e.g.

    SELECT id
      FROM var_list
     WHERE id IS NOT NULL
     GROUP BY id
    

    I probably would not use a MySQL stored program with a CURSOR loop, I'd use a different approach.

    I'd make use of a SELECT statement to generate a set of SQL statements which could be executed. Assuming id is integer type, I would likely do something like this:

    SELECT CONCAT(' SELECT * 
                       INTO OUTFILE ''/tmp/orders_',s.id,'.csv''
                       FIELDS TERMINATED BY '','' ENCLOSED BY ''"''
                       LINES TERMINATED BY ''\n''
                    FROM data
                   WHERE id = ',s.id,'
                   ORDER BY 1;') AS `stmt`
     FROM ( SELECT v.id
              FROM var_list v
             WHERE v.id IS NOT NULL
             GROUP BY v.id
          ) s
    ORDER BY s.id
    

    For every value of id returned from s, the statement returns the text of a SQL SELECT statement that could (and need to) execute. Capturing that into a text file would give me a SQL script I could run.

    0 讨论(0)
  • 2021-01-25 06:05

    Use a simple query like:

    SELECT *
    FROM data
    WHERE id IN (
        SELECT id
        FROM var_list
    )
    
    0 讨论(0)
提交回复
热议问题