¿Cómo usar las declaraciones preparadas dentro de los procedimientos almacenados de MySQL?


16

Estoy usando mysql y necesito usar de alguna manera el curid de columna devuelto por la declaración preparada en la consulta posterior. Utilizo declaraciones preparadas porque, como he leído, es la única forma de pasar una variable a la cláusula LIMIT. Tengo este procedimiento almacenado aquí:

DROP PROCEDURE IF EXISTS fixbalance;
CREATE PROCEDURE fixbalance (userid INT)
  BEGIN
  DECLARE i INT DEFAULT 0;
  DECLARE balance INT DEFAULT 0;
  DECLARE idcnt INT;

  SET idcnt = (SELECT COALESCE(COUNT(id), 0) 
               FROM coupon_operations 
               WHERE user_id = userid);
  IF idcnt <> 0 THEN
    WHILE i <= idcnt DO
      BEGIN
        SET @iter = i;
        SET @user_id = userid; 
        SET @sql = CONCAT('SELECT id AS curid 
                           FROM coupon_operations 
                           WHERE user_id = ? 
                           ORDER BY id ASC 
                           LIMIT ?, 1');
        PREPARE stmt FROM @sql;
        EXECUTE stmt USING @user_id, @iter;
        DEALLOCATE PREPARE stmt;
        SET balance = balance + (SELECT points 
                                 FROM coupon_operations 
                                 WHERE user_id = @user_id 
                                 AND id = @curid);
        UPDATE coupon_operations SET balance = balance;
        SET i = i + 1;
      END;
    END WHILE;
  END IF;
END;
|

Esto no funciona: no estoy seguro de cómo pasar la curid.

Respuestas:


10

La solución fue SET la variable en la declaración preparada como en:

SET @sql = CONCAT('SET @curid = SELECT id
                                FROM coupon_operations 
                                WHERE user_id = ? 
                                ORDER BY id ASC 
                                LIMIT ?, 1');

1
¿Cuál es el punto CONCATen el código anterior?
Pacerier

@Pacerier No necesita usarlo a CONCATmenos que pase el nombre de la tabla o algo así como un parámetro. ex)CONCAT('select * from ', the_table_name, ' where id>100');
Deckard

9

Me alegra que hayas encontrado tu respuesta. Otra solución sería utilizar la sintaxis SELECT ... INTO :

SET @sql = CONCAT('SELECT id INTO @curid FROM coupon_operations 
                   WHERE user_id = ? 
                   ORDER BY id ASC 
                   LIMIT ?, 1');

0

Por favor intente esto. para resolver las declaraciones Concatenate, PREPARE y EXECUTE como se indica a continuación.

CREATE DEFINER=`products`@`localhost` PROCEDURE `generateMeritList`(
   IN `mastercategory_id` INT(11), 
   IN `masterschools_id` INT(11)
 )
 NO SQL
 begin

  declare total int default 0;
  declare conditions varchar(255) default ''; 
  declare finalQuery varchar(60000) default '';

if mastercategory_id > 0 THEN
    SET conditions =  CONCAT(' and app.masterschools_id = ', mastercategory_id);
end if;

SET @finalQuery = CONCAT(
 "SELECT * FROM applications app INNER JOIN masterschools school ON school.id = app.masterschools_id
WHERE 
 (app.status = 'active'", conditions, " LIMIT ",total); 

 PREPARE stmt FROM @finalQuery;
 EXECUTE stmt;
 DEALLOCATE PREPARE stmt;

end

Este ejemplo en particular está bien, pero desaconsejaría usarlo como un patrón general debido al riesgo de inyección de SQL . Si la entrada fuera una cadena, como VARCHAR, en lugar de INT, eso potencialmente permitiría a un atacante inyectar código SQL. Una declaración preparada creada correctamente es más segura que concatenar cadenas con entrada externa.
Juanal
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.