2016-09-17 3 views
0

Ниже приведен пример кода моей хранимой процедуры, в которой я работаю для расчета процентов. Этот код не является исполняемым, поскольку в соответствии с поиском его проблемы при определении создания временного блока таблицы перед объявлением курсора, но если я определяю то же самое недавно после объявления курсора, то он выполняется успешно.Получение двух проблем при использовании хранимой процедуры в MySQL

1- Мой вопрос: я использую эту таблицу внутри курсора, поэтому мне нужно определить после курсора, или я пропустил что-нибудь?

CREATE PROCEDURE `sp_interest_calculation_test`(
    IN sub_type CHAR(1) 
) 
BEGIN 
    DECLARE s_ledger_id INT; 
    DECLARE s_start, s_end, s_tran INT DEFAULT 0; 

    **DROP TABLE IF EXISTS tmp_interest; 
    CREATE TEMPORARY TABLE IF NOT EXISTS tmp_interest(
     id int(11) NOT NULL AUTO_INCREMENT, 
     ledger_id INT UNSIGNED, 
     dr_amount INT, 
     cr_amount INT, 
     balance INT 
    );** 

    DECLARE cur_saving_acc CURSOR FOR 
    SELECT SQL_CALC_FOUND_ROWS 1; 

    OPEN cur_saving_acc; 

    SET s_end = (SELECT FOUND_ROWS()); 

    WHILE s_start<s_end DO 
    FETCH cur_saving_acc INTO s_ledger_id; 

    INSERT INTO tmp_interest(ledger_id) 
    SELECT s_ledger_id; 

    SELECT * FROM tmp_interest; 

    /*Interest calculation logic ends here */ 
    SET s_start = s_start+1; 

    END WHILE; 

    CLOSE cur_saving_acc; 

END 

2- После успешного выполнения выше хранимой процедуры (определение временной таблицы после объявления курсора), я получил следующий вопрос, когда я называю SP:

CALL sp_interest_calculation_test ('A'); 

«Код ошибки: 1075 Неправильный определение таблицы, и может быть только один автоматический столбец, и он должен быть определен как ключ «

Это потому, что я пропустил определение идентификатора в качестве первичного ключа, поэтому я просто изменил столбец с помощью

id int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY 

Я использую хранимую процедуру в MySQL в первый раз, и мне было неловко исправлять вышеупомянутую проблему, так что, пожалуйста, опишите мне вышеперечисленные проблемы и их причины, и там все равно определите колонку автоматического номера, как первичный ключ?

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

Благодарим вас за внимание и надеемся, что вы поинтересуетесь этим вопросом. (Я не нашел ничего конкретного для этих вопросов). Я также ищу альтернативы, просто я должен рассчитать дебет, кредит, баланс для каждого регистра, я пробовал его без цикла в одном запросе, но исполнение никогда не заканчивается.

ответ

1

DECLARE разрешается только внутри BEGIN ... END составного оператора а и должно быть в его начале, перед любыми другими операторами.

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

http://dev.mysql.com/doc/refman/5.7/en/declare.html

Это ограничение.

Теперь обходное решение: Добавить вложенный блок BEGIN ... END.

DELIMITER $$ 
CREATE PROCEDURE ... 
BEGIN 
    DECLARE ... INT ... -- variable 
    CREATE TEMPORARY TABLE... -- following the declarations, no more declarations allowed, unless... 
    BEGIN -- resets the scope, changes the rules, allows more declarations 
    DECLARE ... INT ... -- variables 
    DECLARE ... CURSOR ... 
    DECLARE CONTINUE HANDLER ... 
    OPEN ... 
    ... 
    END; 
END $$ 

Все переменные в наружном блоке все еще находятся в объеме во внутреннем блоке, если другие переменных во внутреннем блоке не имеет противоречивое имя.

А HANDLER во внешнем блоке также доступен для сигналов во внутреннем блоке, если там не объявлен конфликтный обработчик, и в этом случае внутренний обработчик поймает исключение, а внешний дескриптор поймает что-либо, обработчик, включая RESIGNAL.

Разрешены множественные уровни вложенности. Размер thread_stack может быть фактором, но документация неясна. Я выполнял 262,144 байтовых потоковых стека, так как до того, как он был установлен по умолчанию, и никогда не сталкивался с лимитом.

+1

Хороший ответ Майкл – Drew

1

Некоторые замечания о том, что можно с AUTO_INCREMENT установки:

create table t1 
( ai int not null auto_increment, 
    b int primary key 
)ENGINE=InnoDB; 
-- Error 1075: AI must be a key 

create table t2 
( ai int not null auto_increment, 
    b int primary key, 
    key(ai) 
)ENGINE=InnoDB; 

-- This is successful 

create table t3 
( ai int not null auto_increment, 
    b int primary key, 
    c int not null, 
    key(ai,c) 
)ENGINE=InnoDB; 
-- This is successful 

create table t4 
( ai int not null auto_increment, 
    b int primary key, 
    c int not null, 
    key(c,ai) 
)ENGINE=InnoDB; 
-- Error 1075: AI must be a key (ai is not left-most in composite) 

create table t5 
( ai int auto_increment primary key, 
    b int not null, 
    c int not null 
)ENGINE=InnoDB; 
-- Success: This is the PREDOMINANT way of doing it 

create table t6 
( ai int not null AUTO_INCREMENT, 
    b int not null, 
    c int not null, 
    PRIMARY KEY(c,ai) 
)ENGINE=InnoDB; 
-- Error 1075: AI must be a key (ai is not left-most in PK) 

create table t7 
( ai int not null AUTO_INCREMENT, 
    b int not null, 
    c int not null, 
    PRIMARY KEY(ai,c) 
)ENGINE=InnoDB; 
-- Success 

create table t8 
( ai int not null AUTO_INCREMENT, 
    b int not null, 
    c int not null, 
    KEY(ai), 
    KEY(c,ai) 
)ENGINE=InnoDB; 
insert t8(b,c) values(1,2); 
insert t8(b,c) values(33,44); 
select * from t8; 
+----+----+----+ 
| ai | b | c | 
+----+----+----+ 
| 1 | 1 | 2 | 
| 2 | 33 | 44 | 
+----+----+----+ 

Примечание ENGINE = MyISAM ведет себя по-разному, например, позволяя ИИ быть не осталось больше всего.

Stored Proc:

DROP PROCEDURE IF EXISTS `sp_interest_calculation_test`; 
DELIMITER $$ 
CREATE PROCEDURE `sp_interest_calculation_test`(
    IN sub_type CHAR(1) 
) 
BEGIN 
    DECLARE done INT DEFAULT FALSE; 
    DECLARE l_ledger_id INT; 
    DECLARE dr_sum DECIMAL(14,2) DEFAULT 0; 
    DECLARE l_dr_amount, l_cr_amount, l_balance DECIMAL(14,2); 
    DECLARE cur_saving_acc CURSOR 
     FOR SELECT ledger_id, dr_amount, cr_amount, balance FROM tmp_interest order by id; 
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; 

    DROP TABLE IF EXISTS tmp_interest; 
    CREATE TEMPORARY TABLE IF NOT EXISTS tmp_interest(
     id int(11) NOT NULL AUTO_INCREMENT, 
     ledger_id INT UNSIGNED, 
     dr_amount DECIMAL(14,2), 
     cr_amount DECIMAL(14,2), 
     balance DECIMAL(14,2), 
     KEY(id) 
    ); 
    INSERT tmp_interest (ledger_id,dr_amount,cr_amount,balance) VALUES 
    (101,100,0,200),(102,140,0,340),(103,0,50,290); 

    OPEN cur_saving_acc; 
    read_loop: LOOP 
     FETCH cur_saving_acc INTO l_ledger_id, l_dr_amount, l_cr_amount, l_balance; 

     IF done THEN 
      LEAVE read_loop; 
     END IF; 
     SET dr_sum=dr_sum+l_dr_amount; 

    END LOOP; 
    CLOSE cur_saving_acc; 
    SELECT CONCAT('sum of debits=',dr_sum) as outCol; 
END;$$ 
DELIMITER ; 

Тест:

call sp_interest_calculation_test('s'); 
+----------------------+ 
| outCol    | 
+----------------------+ 
| sum of debits=240.00 | 
+----------------------+ 

выше CURSOR установки правильный способ сделать FETCH петли с LEAVE и обработчиком. Петли курсора являются точными. Не связывайтесь с переменной done. Вы доверяете FETCH и обработчику с ним. Так что пусть это имеет дело с done все по-своему.

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

+0

Большое спасибо, но я использовал этот шаблон первым в этой логике, но он создаст так много проблем, иногда он заканчивается во время выполнения автоматически и видел проблему в течение нескольких исполнений. Так что я изменил выше, и он работает нормально с 2-3 месяцев, но я не думаю, что CURSOR в MySQL заслуживает доверия. Что ты говоришь ? если кто-нибудь предложит мне альтернативу, тогда я изменю логику, позвольте мне упомянуть об этом в моем вопросе, это также утомительно для отладки. +1 для вашей очень полезной информации. – Susang

+0

Я бы никогда не использовал шаблон, отличный от того, который я показал, и тот, который был в руководстве. Если вы когда-нибудь захотите, чтобы я просмотрел любые ваши материалы, зайдите в [Campaigns] (http://chat.stackoverflow.com/rooms/95290) чат и пингуйте меня. Я, конечно, никогда не буду использовать 'SQL_CALC_FOUND_ROWS' в этом' while', и это не лучшие методы – Drew

+1

Тогда я уверен, что я что-то пропустил, разница только SET done = TRUE; (ваш) и SET done = 1; мой, и то же самое в выражении if, чтобы ОСТАВИТЬ. это имеет смысл? и я обязательно буду пинговать вас очень скоро, тогда я опишу весь сценарий и мое требование. Еще раз спасибо – Susang

Смежные вопросы