The mysql prepare statement sucks heavy time. You can not (!?!!!) use it to template field and table names. So it is mostly useless for any heavy duty stored procedures that want to address different columns.

Here is a small function that will take care of that :)

SQL:
  1. DELIMITER //
  2. DROP FUNCTION  IF EXISTS substrCount//
  3. CREATE FUNCTION substrCount(x varchar(255), delim varchar(12)) returns int
  4. RETURN (length(x)-length(REPLACE(x, delim, '')))/length(delim);//
  5.  
  6. DROP FUNCTION  IF EXISTS charsplit//
  7. CREATE FUNCTION charsplit(x varchar(255), delim varchar(12), pos int) returns varchar(255)
  8. RETURN REPLACE(substring(substring_index(x, delim, pos), length(substring_index(x, delim, pos - 1)) + 1), delim, '');//
  9.  
  10. DROP FUNCTION  IF EXISTS replacefirst//
  11. CREATE FUNCTION replacefirst(haystack varchar(255), needle varchar(255),replacestr varchar(255)) returns varchar(255)
  12. BEGIN
  13.     IF LOCATE(needle,haystack)>0 THEN
  14.         SET @replaced=concat(LEFT(haystack,LOCATE(needle,haystack)-LENGTH(needle)),replacestr,RIGHT(haystack,LENGTH(haystack)-LOCATE(needle,haystack)));
  15.     ELSE
  16.         SET @replaced=haystack;
  17.     END IF;
  18.     RETURN @replaced;
  19. END;//
  20.  
  21. DROP FUNCTION  IF EXISTS properprepare//
  22. CREATE FUNCTION properprepare(template varchar(255), args varchar(255)) returns varchar(255)
  23. BEGIN
  24.  SET @i=0;
  25.  SET @numargs = substrCount(args,',');
  26.   WHILE @i <= @numargs DO
  27.       SET @i= @i+ 1;
  28.       SET template=replacefirst(template,'?',charsplit(args,',',@i));
  29.   END WHILE;
  30. RETURN template;
  31. END;//
  32.  
  33. DELIMITER ;

Test with

SQL:
  1. SELECT properprepare('SELECT ? FROM ? ', '*,user');
  2. SELECT properprepare('SELECT ? FROM ? ??', '*,user');
  3. SELECT properprepare('SELECT ? FROM ?', '*,user,test,test1');
  4. SELECT properprepare('SELECT ? FROM ? WHERE ?=?', '*,user,user_id,25');

-- So now you can in your stored procedures (at last!) do:

SQL:
  1. PREPARE update_query FROM properprepare('SELECT ? FROM ?', '*,mytable');
  2. EXECUTE update_query;
  3. PREPARE update_query FROM properprepare('SELECT ? FROM ?', 'myfield,mytable');
  4. EXECUTE update_query;

The first argument is the template with the question mark '?' as placeholder, the second is a comma separated string with the variables to replace. If you pass too few variables, the remaining placeholders will not be modified so they can be treated with the mysql PREPARE FROM, EXECUTE USING afterwards...

SQL:
  1. SET @a=25;
  2. SET @b=properprepare('SELECT ? FROM ? WHERE ?=?', '*,user,id_user');
  3. PREPARE stmnt FROM  @b;
  4. EXECUTE stmnt USING @a;

CAVEAT: Hardly tested, do not use in production without really testing this one

One Response to “Use parameters for field and table names in a MySQL Prepare statement to create truly dynamic SQL”

  1. Roland Bouman Says:

    “The mysql prepare statement sucks heavy time. You can not (!?!!!) use it to template field and table names.”

    To my knowledge, no stored procedure language supports this for prepared statements. The entire idea of *preparing* a statement is that the database engine can parse a *statement*, optimize it, create a query plan for it and then afterwards reuse the handle to that statement. A statement template with place holders for structural elements (tables, columns etc) is syntactically not a statement, can thus not be parsed, and thus no plan can be created for it.

    A few comments on your code:

    #1: length() returns the number of *bytes* not the number of characters. There are multiple places in you code where this difference matters.

    #2: Inside your functions you are using user-defined variables (@variables) instead of proper local variables. This is unnecessary, and can have unwanted side effects.

    #3: You do not DEALLOCATE your prepared statements. This might not seem a big deal, but can be killing if you have a high load on your server. That is because the total number of prepared statements is limited.

    kind regards,

    Roland.

Leave a Reply

Creative Commons License
This work is licensed under a Creative Commons Attribution 2.0 License.