Я хочу создать хранимую процедуру MYSQL, которая динамически переименовывает имена столбцов в TABLE_EXAMPLE с содержанием из TABLE_MASTER -> ожидаемый результат отображается в TABLE_RESULT .

Вот содержание таблиц:

    TABLE_EXAMPLE (THE HEADERS MIGHT CHANGE BUT THEY ARE MAPPED WITH A COLUMN IN TABLE_EXAMPLE):

           |header01 | header02|...|header n|
           |data01 ..| data02..|...|data 0n|
           |data11 ..| data12..|...|data 1n|
             ..........etc.................
           |data n1..|data n2..|...|data nn|

    TABLE_MASTER (STATIC TABLE, UNCHANGED):

           |ORIGIN| TARGET| NAME|
           |header01|header_master01|Paul|
           |header02|header_master02|Paul|
            ..........etc.................
           |header n|header_master n|Paul|

Ожидаемый результат содержит данные из TABLE_EXAMPLE, но с сопоставленными именами столбцов, найденными с помощью TABLE_MASTER.TARGET:

         TABLE_RESULT:
            |data_master01|data_master02|...|data_master0n| NAME|
            |data01.......|data02.......|...|data 0n.......|Paul|
            |data11.......|data12.......|...|data 1n.......|Paul|  
            .........................etc.........................
           |data n1..|data n2...........|...|data nn.......|Paul|

PS: Простое: «ALTER TABLE table_example CHANGE COLUMN old new char (250)» не подойдет.

Спасибо за вашу помощь!

РЕДАКТИРОВАТЬ 1: я пытался написать это, но безуспешно, потому что 'oldname' и 'newname' не считаются переменными.

BEGIN

DECLARE n INT(10) ; DECLARE i INT(10) ;
DECLARE oldname VARCHAR(40); DECLARE newname VARCHAR(40);

SET n=(SELECT count(id) FROM `master_table` where `name`='paul');

SET i=1; WHILE i<n DO 

SET oldname=(SELECT `COLUMN_NAME`  FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE 
`TABLE_SCHEMA`='mydb'AND `TABLE_NAME`='table_example' LIMIT 1, 1) ; 
SET newname=(SELECT TARGET FROM MASTER_TABLE WHERE ORIGIN='oldname');

ALTER TABLE `table_example` CHANGE oldname newname VARCHAR(50) NOT NULL;

SET i=i+1 ; END WHILE ;
END
-2
SamanthaAlexandria 13 Окт 2019 в 22:55

3 ответа

Лучший ответ

Я думаю, я понимаю, что вы хотите выбрать столбец по имени, а имена - это строки в вашем TABLE_MASTER.

Вы не можете сделать это в одном запросе SQL, потому что SQL не может выбрать столбец, используя строковое выражение. Есть разница между строкой и идентификатором. Например, это выбирает данные из столбца по идентификатору:

SELECT header01 ...

Но ниже приведено строковое выражение (простое, которое является просто постоянным значением). Он возвращает только фиксированную строку header01, а НЕ данные из столбца с таким именем:

SELECT 'header01' ...

Аналогично, использование любого другого выражения в списке выбора только выбирает значение этого выражения, а НЕ данные, хранящиеся в столбце, названном строковым значением выражения.

Поэтому, если вы хотите, чтобы запрос возвращал динамический столбец, названный другой переменной или выражением, вы не можете сделать это в том же запросе, где читаете это выражение. Вы должны отформатировать новый запрос SQL из значений, которые вы прочитали. Это называется динамический SQL-оператор (уже упоминавшийся spencer7593, который опубликовал ответ, когда я писал свой собственный ответ).

Вы можете использовать ваш TABLE_MASTER для форматирования динамического оператора SQL, чтобы выбрать столбцы и переопределить их псевдоним:

SELECT CONCAT(
  'SELECT ', 
   GROUP_CONCAT(CONCAT(ORIGIN, ' AS ', TARGET)), ', ', 
   QUOTE(MAX(NAME)), ' AS NAME ',
  'FROM TABLE_EXAMPLE'
) INTO @sql
FROM TABLE_MASTER;

Результатом этого является строка, которая формирует другой оператор SELECT, этот переименовывает столбцы, как вы хотите:

SELECT header01 AS header_master01,header02 AS header_master02, 'Paul' AS NAME FROM TABLE_EXAMPLE  

Затем вы можете использовать строку, хранящуюся в @sql, как динамический запрос SQL.

Вот процедура, которая делает это:

DELIMITER ;;

CREATE PROCEDURE MyProc()
BEGIN
    SELECT CONCAT(
      'SELECT ', 
       GROUP_CONCAT(CONCAT(ORIGIN, ' AS ', TARGET)), ', ', 
       QUOTE(MAX(NAME)), ' AS NAME ',
      'FROM TABLE_EXAMPLE'
    ) INTO @sql
    FROM TABLE_MASTER;
    PREPARE stmt FROM @sql;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
END

DELIMITER ;

Вызови процедуру и получи результат:

CALL MyProc();

+-----------------+-----------------+------+
| header_master01 | header_master02 | NAME |
+-----------------+-----------------+------+
| data01          | data02          | Paul |
| data11          | data12          | Paul |
+-----------------+-----------------+------+

Я должен прокомментировать, что это большая проблема, чтобы пройти. Я бы предпочел получить данные, как они есть в базе данных, и переформатировать их в коде моего приложения. Тогда мне не пришлось бы использовать динамический SQL для форматирования столбцов.

2
Bill Karwin 13 Окт 2019 в 22:01

Спецификация не совсем понятна, чего именно мы пытаемся достичь.

Похоже, мы хотим хранимую процедуру, которая возвращает набор результатов.

Это будет означать, что процедура выполнит инструкцию SELECT.

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

  SELECT 'some_expression' AS foo

Набор результатов, возвращаемый этим оператором SELECT, будет содержать столбец с именем foo.

Идентификаторы (имена таблиц, имена столбцов, имена функций), на которые есть ссылка в операторе SELECT, не являются динамическими; они не могут быть изменены динамически при выполнении оператора. Псевдоним, который будет назначен столбцу, должен быть указан в операторе.

  SELECT some_expr AS new_column_name 

И, имея динамическое значение new_column_name и получая его из некоторой таблицы, нам потребуется выполнить отдельный оператор SQL, чтобы получить имя столбца, который будет использоваться.

В рамках процедуры мы можем динамически подготовить текст SQL, который мы хотим выполнить.

Как пример, с чем-то вроде этого в теле хранимой процедуры:

  DECLARE col_name VARCHAR(80);

  SET col_name = 'foo' ;

  SET @sql = CONCAT('SELECT ''some_expr'' AS `',col_name,'`') ;

  PREPARE stmt FROM @sql;
  EXECUTE stmt;
  DEALLOCATE PREPARE stmt;  

Выполнение процедуры приведет к выполнению такого оператора:

  SELECT 'some_expr' AS `foo`

Возвращает набор результатов с именем столбца foo.


Ссылка: https: //dev.mysql. ком / DOC / RefMan / 8.0 / ен / SQL - синтаксис, подготовленный - statements.html

1
spencer7593 13 Окт 2019 в 20:35

Вот хранимая процедура с целью переименования столбцов таблицы table_example в соответствии с отображением, определенным в table_master. Он работает путем извлечения содержимого сопоставления, а затем динамически генерирует серию команд ALTER TABLE ... RENAME COLUMN ... TO ... (обратите внимание, что этот синтаксис поддерживается только в SQL 8.0).

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

Вы раскомментируете оператор SELECT @q и комментируете три следующие строки, чтобы выполнить процедуру и отобразить команды переименования, фактически не выполняя их.

DELIMITER $$
CREATE PROCEDURE RenameColumns()
BEGIN

    DECLARE finished INTEGER DEFAULT 0;
    DECLARE old_col VARCHAR(50);
    DECLARE new_col VARCHAR(50);

    DECLARE curs CURSOR FOR SELECT origin, target FROM table_master;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;

    OPEN curs;
    renameLoop: LOOP

        FETCH curs INTO old_col, new_col;
        IF finished = 1 THEN 
            LEAVE renameLoop;
        END IF;

        SET @q = CONCAT('ALTER TABLE table_example RENAME COLUMN ', old_col, ' TO ', new_col);
        -- SELECT @q;
        PREPARE stmt FROM @q;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt; 

    END LOOP renameLoop;
    CLOSE curs;

END$$
DELIMITER ;

Демонстрация на БД Fiddle

select * from table_master;

| origin   | target          |
| -------- | --------------- |
| header01 | header_master01 |
| header02 | header_master02 |
| header03 | header_master03 |


select * from table_example;

| header01 | header02 | header03 |
| -------- | -------- | -------- |
| 1        | 2        | 3        |


CALL RenameColumns();

select * from table_example;

| header_master01 | header_master02 | header_master03 |
| --------------- | --------------- | --------------- |
| 1               | 2               | 3               |
1
GMB 13 Окт 2019 в 22:26