Wie verwende ich vorbereitete Anweisungen in gespeicherten MySQL-Prozeduren?

16

Ich verwende mysql und muss irgendwie die Spalte curid verwenden, die von der vorbereiteten Anweisung in der späteren Abfrage zurückgegeben wird. Ich verwende vorbereitete Anweisungen, weil ich gelesen habe, dass dies die einzige Möglichkeit ist, eine Variable an die LIMIT-Klausel zu übergeben. Ich habe diese gespeicherte Prozedur hier:

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;
|

Das funktioniert nicht - ich bin mir nicht sicher, wie ich den Curid überwinden soll.

Donk
quelle

Antworten:

10

Die Lösung bestand darin, die Variable in der vorbereiteten Anweisung selbst wie folgt zu setzen:

SET @sql = CONCAT('SET @curid = SELECT id
                                FROM coupon_operations 
                                WHERE user_id = ? 
                                ORDER BY id ASC 
                                LIMIT ?, 1');
Donk
quelle
1
Worum geht es CONCATim obigen Code?
Pacerier
@Pacerier Sie müssen nicht verwenden, es CONCATsei denn, Sie übergeben den Tabellennamen oder etwas als Parameter. ex)CONCAT('select * from ', the_table_name, ' where id>100');
Deckard
9

Ich bin froh, dass du deine Antwort gefunden hast. Eine andere Lösung wäre die Verwendung der SELECT ... INTO- Syntax:

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

Bitte versuchen Sie es. Verketten, PREPARE und EXECUTE Anweisungen wie folgt zu lösen ..

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
Dinesh Vaitage
quelle
Dieses Beispiel ist in Ordnung, aber ich würde davon abraten, es als allgemeines Muster zu verwenden, da das Risiko einer SQL-Injection besteht . Wenn die Eingabe eine Zeichenfolge wie VARCHAR anstelle von INT wäre, könnte ein Angreifer möglicherweise SQL-Code einschleusen. Eine ordnungsgemäß erstellte vorbereitete Anweisung ist sicherer als das Verketten von Zeichenfolgen mit externen Eingaben.
Juanal