sql >> Base de Datos >  >> RDS >> Mysql

Declaración preparada de procedimiento almacenado de MySQL (SQL dinámico) parametrizada

El EXECUTE declaración debe recibir una lista fija de argumentos, por lo que tendrá que preparar y ejecutar la sentencia en un IF/THEN/ELSE bloquear.

IF articlesModule = 1 THEN
    SET @query = ... UNION ...
    PREPARE stmt FROM @query;
    EXECUTE stmt USING @searchWordIn, @searchWordIn, @searchWordIn, @searchWordIn;
ELSE
    SET @query = ...; /* no UNION */
    PREPARE stmt FROM @query;
    EXECUTE stmt USING @searchWordIn, @searchWordIn;
END IF;

No conozco ninguna forma de resolver esto en el alcance limitado del lenguaje de procedimiento almacenado MySQL. Para mí, es otra buena razón para no usar SQL dinámico en procedimientos almacenados.

Re sus comentarios:

Ya veo... podrías usar un CASE declaración en lugar de IF/THEN/ELSE , pero en realidad tiene 2 =128 posibles casos diferentes para cadenas de consulta, porque asumo que cualquiera de esos 7 módulos podría buscarse o no.

Una alternativa que te permitiría usar parámetros de consulta es olvidarte de usar UNION , y en su lugar escriba el procedimiento de tal manera que se ejecute hasta 7 SELECT separados consultas y las devuelve todas como conjuntos de resultados múltiples . Eso es algo que los procedimientos almacenados están destinados a hacer. Pero debe escribir código en su capa de PHP para obtener cada conjunto de resultados a su vez. Es decir, recorrer los conjuntos de resultados y, dentro de ese ciclo, recorrer las filas del conjunto de resultados actual. Ver ejemplo en PDO::nextRowset() o mysqli::next_result() .

¡No, no estás a salvo si haces eso! Usar un parámetro de consulta en PHP para pasar una cadena a CALL WEBSITE_mainSearch(?) es inútil para proteger contra la inyección de SQL, si luego concatena ese valor de parámetro en otra cadena dentro del procedimiento y realiza un análisis y ejecución de SQL dinámico. El uso de parámetros de consulta no hace que los valores de los parámetros sean "seguros", simplemente separan esos valores de la fase de análisis de SQL.

Está más seguro si usa la función incorporada de MySQL COTIZACIÓN() al concatenar las cadenas. QUOTE() escapa de caracteres especiales, al igual que mysql_real_escape_string() . Excepto que es ligeramente diferente, porque también produce las comillas simples que delimitan la cadena, como PDO::cita() lo hace.

SET @query = CONCAT(@query, 'SELECT blockName AS itemName, blockPath AS seoName, 
  blockID AS itemID, MATCH(blockName, blockBody) AGAINST (',
  QUOTE(searchWordIn), ') AS relevance, \'block\' AS itemType 
  FROM content_blocks WHERE MATCH(blockName, blockBody) AGAINST (',
  QUOTE(searchWordIn),')') ;

Actualización:una alternativa más:use UNION para agregar más subconsultas y llevar la cuenta de los módulos. Luego usa un CASE para ejecutar la consulta preparada con un número diferente de parámetros en función del recuento acumulado.

SET @n = 0;
IF articlesModule = 1 THEN
    SET @query = ... UNION ...
    SET @n = @n+1;
END IF;

IF newsModule = 1 THEN
    SET @query = ... UNION ...
    SET @n = @n+1;
END IF;

... and similar for the other 5 modules ...

PREPARE stmt FROM @query;

CASE @n
WHEN 1:
    EXECUTE stmt USING @searchWordIn, @searchWordIn;
WHEN 2:
    EXECUTE stmt USING @searchWordIn, @searchWordIn, @searchWordIn, @searchWordIn;
WHEN 3:
    EXECUTE stmt USING @searchWordIn, @searchWordIn, @searchWordIn, @searchWordIn,
      @searchWordIn, @searchWordIn;
WHEN 4:
    EXECUTE stmt USING @searchWordIn, @searchWordIn, @searchWordIn, @searchWordIn,
      @searchWordIn, @searchWordIn, @searchWordIn, @searchWordIn;
WHEN 5:
    EXECUTE stmt USING @searchWordIn, @searchWordIn, @searchWordIn, @searchWordIn,
      @searchWordIn, @searchWordIn, @searchWordIn, @searchWordIn,
      @searchWordIn, @searchWordIn;
WHEN 6:
    EXECUTE stmt USING @searchWordIn, @searchWordIn, @searchWordIn, @searchWordIn,
      @searchWordIn, @searchWordIn, @searchWordIn, @searchWordIn,
      @searchWordIn, @searchWordIn, @searchWordIn, @searchWordIn;
WHEN 7:
    EXECUTE stmt USING @searchWordIn, @searchWordIn, @searchWordIn, @searchWordIn,
      @searchWordIn, @searchWordIn, @searchWordIn, @searchWordIn,
      @searchWordIn, @searchWordIn, @searchWordIn, @searchWordIn, 
      @searchWordIn, @searchWordIn;
END;