我正在编写一个存储过程,它将游标打开到表中,然后遍历所有记录。在迭代过程中,我根据第一个游标的结果创建一个动态查询。我需要在动态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中
答案 0 :(得分:4)
创建另一个过程并在这个新过程中编写游标代码,然后调用你想要声明游标的过程......
答案 1 :(得分:3)
不允许使用DEFINE cur CURSOR FOR prepared_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 ;
答案 2 :(得分:1)
由于您无法使用光标进行动态查询,因为SET
之前无法DECLARE
。此外,您无法将存储过程CALL
与CURSOR FOR
DECLARE cursor_name CURSOR FOR select_statement
CALL
不是 select_statement 。
作为解决方法:
您应该创建3个过程而不是1个。
CURSOR FOR
SELECT
FROM
临时表。但是您应该首先确保运行临时表/视图过程 - 以获得更新的结果。你不能CALL
DECLARE
光标之前的程序。CALL
这就是你需要第三步的原因。CALL
生成临时表/视图的过程,然后{{1}}计算结果的预期过程。您应该最后使用最后一个程序作为执行结果的程序。答案 3 :(得分:0)
我在您的脚本中看到了两个可能的问题:
1)“DECLARE cursor2 CURSOR FOR stmt;”在任何可执行语句之前,可能需要使用所有其他声明将其移动到过程的顶部。
2)游标不能基于动态SQL(即我认为你不能在准备好的语句上构建它)。要解决此限制,可以基于视图声明游标,然后在打开游标之前使用动态SQL创建视图。这种方法的问题是视图是公共的 - 游标声明必须具有视图的固定名称,因此多个并发用户可能无意中看到其他人动态定义的视图。我的解决方法是检查视图的存在并延迟执行该过程,直到视图被删除。这意味着为了在繁忙的环境中可行,您应该创建视图,循环光标,然后尽快删除视图。技术上并不优雅,但这种方法在我的低流量情况下起作用,并且避免了临时表的开销。或者,正如其他人所建议的那样,临时表是线程安全的,但可能会影响性能。
答案 4 :(得分:0)
“准备视图”方法的替代方案在运行该过程时需要更多的CPU周期和内存以及额外的磁盘空间。
答案 5 :(得分:0)
该线程对我有很大帮助,所以这是我的答案,即如何使用一个表中的值通过查询迭代并存储到第二个表或视图中。
lastName = ["Smith", "Jones", "Brown", "Miller", "Brown", "Williams"]
phone = ["111-123-1234","222-123-1234","333-123-1234","444-123-1234","555-123-1234","662-123-1234"]
sample_name = 'David Smith'
def find_details(full_name):
# Split the full name into first and last name
[first, last] = full_name.split(' ')
# check for the first name in the name list
for count, f_name in enumerate(name):
# if the first name is listed
if f_name == first:
# check if the last name at the same index is the last name we need
if lastName[count] == last:
phone_number = phone[count]
# we have found the person we need
print(f'{full_name} is located at position {count}. \nPhone number: {phone_number}\n')
return True
# Otherwise, we didn't find the name
print(f"The name: {full_name} does not exist in our records. \nPlease try a different name.\n")
return False
find_details('Josh Brown')
find_details('Delilah Le')
答案 6 :(得分:0)
我们可以考虑使用 stmt
来解决这种情况:
统计查询将返回的所有记录
循环浏览查询返回的每条记录,为此使用限制。
见下面的例子:
CREATE PROCEDURE `proc_example`(IN p_where text)
BEGIN
DECLARE v_where text default "";
DECLARE v_cont integer default 0;
#build a dynamic where
set v_where = p_where;
#Count query records
set @v_sqlSelect_count = 'select count(*) into @v_total ';
set @v_sqlSelect_count = concat(@v_sqlSelect_count,'from table ');
set @v_sqlSelect_count = concat(@v_sqlSelect_count,'where ');
set @v_sqlSelect_count = concat(@v_sqlSelect_count,v_where);
#Executa query
PREPARE stmt_total FROM @v_sqlSelect_count;
EXECUTE stmt_total;
DEALLOCATE PREPARE stmt_total;
#if exists records
if (@v_total > 0) then
set v_cont = 0;
navRecords:loop
if (v_cont > (@v_total - 1)) then
leave getAgend;
end if;
#build select
set @v_sqlSelect = 'select id,name ';
set @v_sqlSelect = concat(@v_sqlSelect,'into @id,@name ');
set @v_sqlSelect = concat(@v_sqlSelect,'from table ');
set @v_sqlSelect = concat(@v_sqlSelect,'where ');
set @v_sqlSelect = concat(@v_sqlSelect,v_where);
set @v_sqlSelect = concat(@v_sqlSelect,' order by id asc limit ',v_cont,',1');
#Execute query
PREPARE stmt_select FROM @v_sqlSelect;
EXECUTE stmt_select;
DEALLOCATE PREPARE stmt_select;
#Do anything with the data @id, @name
update table1 set desc1 = @name where id1 = @id;
#Next record
set v_cont = v_cont + 1;
end loop navRecords;
end if;
END