【问题标题】:How do I create a variable cursor inside a store procedure?如何在存储过程中创建变量游标?
【发布时间】:2013-11-08 17:37:07
【问题描述】:

我正在编写一个过程来处理一个名称取决于日期的 MySQL 表,格式为cdr_20131108。我的程序应该能够从任何给定日期的表中执行一些操作。

所有这些表都具有相同的结构,并包含一个time 字段。对于每一行,我需要检查前一小时有多少行满足某些条件。据我所知,我需要一个游标来查找当前行的时间,以及更多的数据,然后准备并执行一个查询,从中可以找到我的结果。

在伪代码中:

DELIMITER $$

CREATE DEFINER=`root`@`localhost` PROCEDURE `myproc`()
BEGIN

DECLARE all necessary variables to save the data returned by the cursor    

DECLARE c1 cursor for
    select required fields
    from cdr_20131103
    where some criteria;

DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET _done = TRUE;

set @tablename := concat('rm_cdrs',date_format(curdate(),'%Y%m%d'));
set @entries := 0;

open c1;
c1_loop: loop
    fetch c1 into my variables;
    if `_done` then leave c1_loop; end if;      
    set @q := concat('... my query is prepared here: select count(*) into @variable (
                        select in which i look for rows that meet my criteria that 
                        happened less tan an hour before the one in the cursor)');

    PREPARE stmt FROM @q;    EXECUTE stmt; DEALLOCATE PREPARE stmt;

  if some criteria is met then do some stuff...

end loop c1_loop;
close c1;

END

所以当我需要在不调整代码的情况下检查今天的表格时,我的问题就出现了,因为 MySQL 不允许我在这里使用变量:

DECLARE c1 cursor for
select required fields
from cdr_20131103
where some criteria;

我尝试在此处创建一个使用正确表名生成所需过程的过程,但 MySQL 不允许我从存储例程中删除或更改过程。

在这种情况下有什么解决方法吗?

  • 在这种情况下我可以避免使用光标吗?

  • 我是否应该按照here 的说明手动执行光标的任务?

  • 此过程可能会使用 cron 定期调用。我应该只编写一些 Java/C/PHP 应用程序,在删除旧应用程序后创建并调用正确的过程吗?

非常感谢!


编辑:

@Sebas 声明可以通过使用单个插入-选择语句来执行此操作。尽管他回答中的视图技巧很有效,但我想尝试并从中学习。我将添加更多信息:

在选择有趣的列并使用WHERE子句过滤掉数据时,@ 987654327如下所示:

+---------+----------------+--------+
| user_ID | destination_ID |  time  |
+---------+----------------+--------+
|       2 |             56 | 110312 |
|       4 |             53 | 110513 |
|       2 |             56 | 110821 |
|       2 |             56 | 113212 | *
|       2 |             56 | 123001 |
+---------+----------------+--------+

我需要找出同一个 user_ID 在一个小时内访问同一个 destination_ID 至少 3 次的时间。因此,* 的行应与 now() 字段一起插入到另一个表中。

我的线性思维告诉我应该逐一处理行,计算初始时间(time - interval 1 hour,在该时间间隔中选择具有相同 User_ID 和 destination_ID 的行,计算它们并最终将它们插入到另一个表中。

在 SQL 中有没有更好的方法来做到这一点?

非常感谢!

【问题讨论】:

  • 我们可能会找到更简单的解决方法。 if some criteria is met then do some stuff...什么东西?
  • 那么插入/选择呢?通过包含计数和游标查询的全局选择,您可以在任何符合您条件的表中插入数据。一站式查询:dev.mysql.com/doc/refman/5.0/en/insert-select.html
  • 当然可以。有时mysql是有限的,但我认为我们可以解决它
  • 你的系统是h24还是h12?还有,time的列类型是什么
  • 您需要在另一个表中插入time 值还是只是NOW()

标签: mysql sql stored-procedures cursor


【解决方案1】:

我认为你可以用一个select insert SQL 做你想做的一切。调查这样的事情(假设tabletoinsert,假设time的列类型是VARCHAR

INSERT INTO tabletoinsert
SELECT user_id, destination_id, SUBSTRING(time, 1, 2) AS hour, NOW()
FROM cdr_20131103
GROUP BY user_id, destination_id, SUBSTRING(time, 1, 2)
HAVING COUNT(*) >= 3

但是如果你真的需要一个游标,你也可以使用一个肮脏的技巧:

1- 在另一个存储过程中动态创建视图

SET @dyn_sql = CONCAT('CREATE OR REPLACE VIEW `v_yourview` AS ', 
                               SELECT required fields
                               FROM cdr_', date_format(curdate(),'%Y%m%d'), ' WHERE some criteria;');

PREPARE stmt_dyn_view FROM @dyn_sql;
EXECUTE stmt_dyn_view;
DEALLOCATE PREPARE stmt_dyn_view;

2- 在主程序的光标中使用它:

DECLARE c1 cursor for
    select required fields
    from v_yourview
    where some criteria;

【讨论】:

  • 非常感谢!我让它使用视图工作。这有效率吗?使用视图为我节省了很多行,并减少了我需要在查询中检查的条件数量。
  • 是的,视图与底层查询一样有效。它没有添加任何存储要求,只是 CREATE VIEW GRANT(或者 CREATE TABLE 就足够了?)。
  • 嗨@Sebas!感谢您的宝贵帮助。我已经尝试了一段时间。虽然这是在自然时间(例如 11:00-11:59)中查找条目的一种非常巧妙的方法,但我认为它不会在其他情况下检测到它,例如 11:30-12:20。我试图通过使用having count(*)>=3 and timediff(max(time(mytime)),min(time(mytime)))<'010000' 来实现这一点。当我在 10:02、10:05 和 10:50 有条目时,这可以正常工作;但是如果我在 11:20 添加另一个失败。我明白为什么了。有没有办法解决这个问题?
  • 我不太明白。为什么不使用 SUBSTR 版本?
猜你喜欢
  • 1970-01-01
  • 2018-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多