MySQL SELECT * не работает с временной таблицей, созданной PREPARE с использованием динамических имен столбцов и таблиц после первого прохода

MySQL 5.2, CentOS 6.4.

MySQL SELECT * терпит неудачу во временной таблице, созданной PREPARE с использованием динамических имен столбца и таблицы после первого прохода, когда имя столбца и имя таблицы изменены на разные значения с первого прохода.

Обходной путь заключается в использовании псевдонима столбца, который остается неизменным от прохода к проходу.

DROP PROCEDURE IF EXISTS test1;
DELIMITER $$
CREATE PROCEDURE test1( column_name VARCHAR(20), table_name VARCHAR(20) )
BEGIN
    SET @prepared_stmt_arg = 'prepared_stmt_arg_value';

    DROP TABLE IF EXISTS tmp1;
    CREATE TEMPORARY TABLE tmp1
        SELECT 1 AS col_tmp1;

    DROP TABLE IF EXISTS tmp2;
    CREATE TEMPORARY TABLE tmp2
        SELECT 2 AS col_tmp2;

    # drop tmp table if it exists
    DROP TABLE IF EXISTS tmp_test1;

    # prepared statement
    SET @prepared_stmt = 
        CONCAT("
            CREATE TEMPORARY TABLE tmp_test1
                SELECT ? AS prepared_stmt_arg, ", column_name, " # AS constant_col_alias
                    FROM ", table_name, "
            "); # END statement

    # display prepared statement before executing it
    SELECT @prepared_stmt;

    # prepare the statement
    PREPARE ps FROM @prepared_stmt;

    # execute
    EXECUTE ps USING @prepared_stmt_arg;

    # deallocate
    DEALLOCATE PREPARE ps;

    # display
    SELECT * FROM tmp_test1;

END $$
DELIMITER ;

Оператор SELECT в самом конце процедуры завершается ошибкой. (Возможно, вам придется прокрутить вниз, чтобы увидеть сообщение об ошибке.)

mysql> CALL test1('col_tmp1', 'tmp1');
+---------------------------------------------------------------------------------------------------------------------------------+
| @prepared_stmt                                                                                                                  |
+---------------------------------------------------------------------------------------------------------------------------------+
|
                                CREATE TEMPORARY TABLE tmp_test1
                                        SELECT ? AS prepared_stmt_arg, col_tmp1 # AS constant_col_alias
                                                FROM tmp1
                                 |
+---------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

+-------------------------+----------+
| prepared_stmt_arg       | col_tmp1 |
+-------------------------+----------+
| prepared_stmt_arg_value |        1 |
+-------------------------+----------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

mysql> CALL test1('col_tmp2', 'tmp2');
+---------------------------------------------------------------------------------------------------------------------------------+
| @prepared_stmt                                                                                                                  |
+---------------------------------------------------------------------------------------------------------------------------------+
|
                                CREATE TEMPORARY TABLE tmp_test1
                                        SELECT ? AS prepared_stmt_arg, col_tmp2 # AS constant_col_alias
                                                FROM tmp2
                                 |
+---------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

ERROR 1054 (42S22): Unknown column 'dev.tmp_test1.col_tmp1' in 'field list'

Однако, если раскомментировать псевдоним столбца (удалить # непосредственно перед AS constant_col_alias), все будет работать хорошо. (Возможно, вам придется прокрутить вниз, чтобы увидеть Query OK.)

mysql> CALL test1('col_tmp1', 'tmp1');
+-------------------------------------------------------------------------------------------------------------------------------+
| @prepared_stmt                                                                                                                |
+-------------------------------------------------------------------------------------------------------------------------------+
|
                                CREATE TEMPORARY TABLE tmp_test1
                                        SELECT ? AS prepared_stmt_arg, col_tmp1 AS constant_col_alias
                                                FROM tmp1
                                 |
+-------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

+-------------------------+--------------------+
| prepared_stmt_arg       | constant_col_alias |
+-------------------------+--------------------+
| prepared_stmt_arg_value |                  1 |
+-------------------------+--------------------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

mysql> CALL test1('col_tmp2', 'tmp2');
+-------------------------------------------------------------------------------------------------------------------------------+
| @prepared_stmt                                                                                                                |
+-------------------------------------------------------------------------------------------------------------------------------+
|
                                CREATE TEMPORARY TABLE tmp_test1
                                        SELECT ? AS prepared_stmt_arg, col_tmp2 AS constant_col_alias
                                                FROM tmp2
                                 |
+-------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

+-------------------------+--------------------+
| prepared_stmt_arg       | constant_col_alias |
+-------------------------+--------------------+
| prepared_stmt_arg_value |                  2 |
+-------------------------+--------------------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

person Michael    schedule 25.08.2013    source источник
comment
Вы действительно хотели иметь столбец с именем ? или вы хотели, чтобы он был заполнителем?   -  person peterm    schedule 26.08.2013
comment
Просто заполнитель. Я поместил его сюда только для того, чтобы проиллюстрировать, что сам подготовленный оператор работает правильно. Я удалю это, так как это не относится к проблеме.   -  person Michael    schedule 26.08.2013


Ответы (1)


Ну это похоже баг или фича (если хотите) до версии 5.6.

См. Ошибка № 32868. Сохраненные подпрограммы не обнаруживают изменения в метаданных.

Обходной путь: очистить сохраненный кэш подпрограмм, выполнив следующие действия:
CREATE OR REPLACE VIEW tmpview AS SELECT 1;

Вот SQLFiddle демонстрация MySql 5.1.X
Здесь SQLFiddle демо MySql 5.5.X

Если вы закомментируете CREATE OR REPLACE VIEWtmpviewAS SELECT 1, вы получите свою ошибку.

Вот SQLFiddle демо MySql 5.6.X показывает, что это не больше не проблема


Теперь у вас есть как минимум следующие варианты:

  1. не используйте SELECT * вместо этого используйте явные имена столбцов.
  2. использовать предложенный обходной путь
  3. обновить до 5.6.Х
person peterm    schedule 25.08.2013