Создание курсора с помощью динамического SQL в MYSQL - PullRequest
2 голосов
/ 21 мая 2011

Я пишу хранимую процедуру, которая открывает курсор к таблице и затем перебирает все записи.В процессе итерации я создаю динамический запрос на основе результатов первого курсора.Мне нужно открыть курсор на динамическом sql, но MySQL не позволяет мне это делать, так как в соответствии с официальным документом mysql "Курсоры должны быть объявлены до объявления обработчиков. Переменные и условия должны быть объявлены дообъявляя либо курсоры, либо обработчики ".Вот скрипт

DELIMITER $$

DROP PROCEDURE IF EXISTS sp_test$$

CREATE PROCEDURE `sp_test`()
BEGIN
    -- Declarations

    DECLARE prepared_sql VARCHAR(1000);
    DECLARE index_count INT;

    -- Cursors
    DECLARE cursor1 CURSOR FOR SELECT * from table1;
    -- Continue Handler for Cursor
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_rows = TRUE;
    -- Open cursors
    OPEN cursor1;

    -- Business Logic
    all_alerts_loop: LOOP
        -- Fetch record from cursor1 and create a dynamic sql

        -- Check if cursor has reached to end than leave the loop
        IF no_more_rows THEN
            LEAVE all_alerts_loop;
        END IF;


        WHILE @some_other_variable <> 0
        DO
                              -- I want to open cursor 2 on this sql
            -- set @prepared_sql =  'create dynamic sql here';  
                    END WHILE;

                    -- This works fine
        PREPARE stmt FROM @prepared_sql;
        EXECUTE stmt;

                    -- But can't define cursor here? so what is the solution
                    -- Gives syntax error, I have tried with @prepared_sql also rather than stmt
        DECLARE cursor2 CURSOR FOR stmt;

    END LOOP;

    -- closing cursors
    CLOSE cursor1;
    END$$

DELIMITER ;

Есть идеи, как создать курсор для динамического запроса?в MYSQL

Ответы [ 5 ]

4 голосов
/ 11 октября 2011

Создайте еще одну процедуру и напишите код курсора в этой новой процедуре, а затем вызовите процедуру, откуда вы хотите объявить курсор ...

3 голосов
/ 11 мая 2018

Недопустим DEFINE cur CURSOR FOR prepare_statement, вы должны определить допустимый оператор SQL.Хорошей новостью является то, что вы можете определить курсор на представлении, которое может быть динамически создано позже.Например ...

DROP PROCEDURE IF EXISTS my_dynamic_proc;
DELIMITER //
CREATE PROCEDURE my_dynamic_proc(tablename varchar(64), fieldname varchar(64), country VARCHAR(64))
BEGIN
    DECLARE adr_value varchar(500);
    DECLARE done BOOLEAN DEFAULT FALSE;
    -- Cursor definition
    DECLARE cur1 CURSOR FOR SELECT address FROM tmp_view_address;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    -- Dynamic view definition and creation
    SET @v = concat('CREATE OR REPLACE VIEW tmp_view_address as SELECT `',fieldname,'` as address FROM ',tablename,' WHERE country_name = "',country,'" group by 1 order by count(1) desc');
    PREPARE stm FROM @v;
    EXECUTE stm;
    DEALLOCATE PREPARE stm;
    -- Open cursor
    OPEN cur1;
    read_loop: LOOP
      FETCH cur1 INTO adr_value;
      IF done THEN
        LEAVE read_loop;
      END IF;
      -- Basic output result
      SELECT concat("My address is ",adr_value);
      -- Use every result in a dynamic update
      SET @u = concat('update ',tablename,' set new_field_address = "',adr_value,'" where country_name = "',country,'" and new_field_address is null');
      PREPARE stm FROM @u;
      EXECUTE stm;
      DEALLOCATE PREPARE stm;
  END LOOP;
  CLOSE cur1;
END//
DELIMITER ;
1 голос
/ 31 марта 2015

Поскольку вы не можете использовать динамические запросы с курсором, потому что вы не можете SET до DECLARE.Также вы не можете использовать хранимые процедуры CALL с CURSOR FOR

ОБЪЯВЛЯТЬ имя курсора CURSOR FOR select_statement

CALL не является select_statement .

В качестве обходного пути:
Вы должны создать 3 процедуры вместо только 1.

  1. Временные таблицы /генератор представлений
    Напишите хранимую процедуру для создания временных таблиц или представлений для ваших динамических запросов.
  2. Расчет результатов
    В вашей текущей процедуре будет использоваться CURSOR FOR SELECT FROM временные таблицы.Но вы должны убедиться, что сначала запускаете процедуру временных таблиц / представлений - чтобы получить обновленные результаты.И вы не можете CALL процедуру перед DECLARE курсором.По этой причине вам потребуется третий шаг.
  3. Запуск всего вместе Последняя хранимая процедура для CALL процедуры, генерирующей временные таблицы / представления, а затем CALL вашей предполагаемой процедуры дляпосчитать результаты.Наконец, вы должны использовать эту последнюю процедуру как ту, которая выполняет ваш результат.
0 голосов
/ 20 июля 2014

подход Карни менее громоздким. Создайте два или более SP, чтобы удовлетворить каждую условную ветвь (для каждой из которых потребовался бы динамический sql). Создайте SP-упаковщик и разнесите вызовы от этого SP к «ответным» SP.

Альтернатива, которая заключается в подходе «Подготовленное представление», требует больше циклов ЦП, памяти и дополнительного дискового пространства при выполнении процедуры.

0 голосов
/ 01 февраля 2013

Я вижу 2 возможные проблемы в вашем скрипте:

1) "ОБЪЯВИТЬ КУРСОР cursor2 ДЛЯ STMT;" вероятно, необходимо переместить его в начало процедуры со всеми другими объявлениями перед выполнением любых исполняемых операторов.

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

...