在 MySQL 中循环结果集

发布于 2024-08-11 12:26:47 字数 849 浏览 5 评论 0原文

我正在尝试在 MySQL 中编写一个存储过程,它将执行一个简单的选择查询,然后循环结果以决定是否执行其他查询、数据转换或完全丢弃数据。实际上,我想实现这个:

$result = mysql_query("SELECT something FROM somewhere WHERE some stuff");
while ($row = mysql_fetch_assoc($result)) {
    // check values of certain fields, decide to perform more queries, or not
    // tack it all into the returning result set
}

只是,我只希望它在 MySQL 中,所以它可以作为过程调用。我知道对于触发器,有 FOR EACH ROW ... 语法,但我找不到在 CREATE TRIGGER ... 之外使用类似的内容。代码>语法。我已经阅读了 MySQL 中的一些循环机制,但到目前为止我所能想象的就是我将实现这样的东西:

SET @S = 1;
LOOP
    SELECT * FROM somewhere WHERE some_conditions LIMIT @S, 1
    -- IF NO RESULTS THEN
    LEAVE
    -- DO SOMETHING
    SET @S = @S + 1;
END LOOP

尽管这在我的脑海中有些模糊。

作为参考,虽然我认为这不一定相关,但初始查询将把四个表连接在一起以形成层次结构权限模型,然后根据特定权限在链上的高度,它将检索有关的附加信息应继承该许可的子女。

I am trying to write a stored procedure in MySQL which will perform a somewhat simple select query, and then loop over the results in order to decide whether to perform additional queries, data transformations, or discard the data altogether. Effectively, I want to implement this:

$result = mysql_query("SELECT something FROM somewhere WHERE some stuff");
while ($row = mysql_fetch_assoc($result)) {
    // check values of certain fields, decide to perform more queries, or not
    // tack it all into the returning result set
}

Only, I want it only in MySQL, so it can be called as a procedure. I know that for triggers, there is the FOR EACH ROW ... syntax, but I can't find mention of anything like this for use outside of the CREATE TRIGGER ... syntax. I have read through some of the looping mechanisms in MySQL, but so far all I can imagine is that I would be implementing something like this:

SET @S = 1;
LOOP
    SELECT * FROM somewhere WHERE some_conditions LIMIT @S, 1
    -- IF NO RESULTS THEN
    LEAVE
    -- DO SOMETHING
    SET @S = @S + 1;
END LOOP

Although even this is somewhat hazy in my mind.

For reference, though I don't think it's necessarily relevant, the initial query will be joining four tables together to form a model of hierarchal permissions, and then based on how high up the chain a specific permission is, it will retrieve additional information about the children to which that permission should be inherited.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

自找没趣 2024-08-18 12:26:47

像这样的事情应该可以解决问题(但是,请阅读代码片段以获取更多信息)

CREATE PROCEDURE GetFilteredData()
BEGIN
  DECLARE bDone INT;

  DECLARE var1 CHAR(16);    -- or approriate type
  DECLARE var2 INT;
  DECLARE var3 VARCHAR(50);
  
  DECLARE curs CURSOR FOR  SELECT something FROM somewhere WHERE some stuff;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET bDone = 1;

  DROP TEMPORARY TABLE IF EXISTS tblResults;
  CREATE TEMPORARY TABLE IF NOT EXISTS tblResults  (
    --Fld1 type,
    --Fld2 type,
    --...
  );

  OPEN curs;

  SET bDone = 0;
  REPEAT
    FETCH curs INTO var1, var2, var3;

    IF whatever_filtering_desired
       -- here for whatever_transformation_may_be_desired
       INSERT INTO tblResults VALUES (var1, var2, var3);
    END IF;
  UNTIL bDone END REPEAT;

  CLOSE curs;
  SELECT * FROM tblResults;
END

需要考虑的一些事情...

关于上面的代码片段:

  • 可能希望将部分查询传递给存储过程,也许特别是搜索条件,以使其更加通用。
  • 如果此方法要由多个会话调用,则可能需要传递某种会话 ID 来创建唯一的临时表名称(实际上不必要的关注,因为不同的会话不共享相同的临时文件命名空间;请参阅下面 Gruber 的评论)
  • 需要正确指定一些部分,例如变量声明、SELECT 查询等。

更一般地说:尝试避免需要游标

我特意将游标变量命名为 curs[e],因为游标是喜忧参半的。它们可以帮助我们实现复杂的业务规则,这些规则可能很难用 SQL 的声明式形式来表达,但它随后让我们使用 SQL 的过程式(命令式)形式,这是 SQL 的一个普遍特征,但它既不是很友好/富有表现力、编程方面,但性能方面往往效率较低。

也许您可以考虑在“普通”(声明性)SQL 查询的上下文中表达所需的转换和过滤。

Something like this should do the trick (However, read after the snippet for more info)

CREATE PROCEDURE GetFilteredData()
BEGIN
  DECLARE bDone INT;

  DECLARE var1 CHAR(16);    -- or approriate type
  DECLARE var2 INT;
  DECLARE var3 VARCHAR(50);
  
  DECLARE curs CURSOR FOR  SELECT something FROM somewhere WHERE some stuff;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET bDone = 1;

  DROP TEMPORARY TABLE IF EXISTS tblResults;
  CREATE TEMPORARY TABLE IF NOT EXISTS tblResults  (
    --Fld1 type,
    --Fld2 type,
    --...
  );

  OPEN curs;

  SET bDone = 0;
  REPEAT
    FETCH curs INTO var1, var2, var3;

    IF whatever_filtering_desired
       -- here for whatever_transformation_may_be_desired
       INSERT INTO tblResults VALUES (var1, var2, var3);
    END IF;
  UNTIL bDone END REPEAT;

  CLOSE curs;
  SELECT * FROM tblResults;
END

A few things to consider...

Concerning the snippet above:

  • may want to pass part of the query to the Stored Procedure, maybe particularly the search criteria, to make it more generic.
  • If this method is to be called by multiple sessions etc. may want to pass a Session ID of sort to create a unique temporary table name (actually unnecessary concern since different sessions do not share the same temporary file namespace; see comment by Gruber, below)
  • A few parts such as the variable declarations, the SELECT query etc. need to be properly specified

More generally: trying to avoid needing a cursor.

I purposely named the cursor variable curs[e], because cursors are a mixed blessing. They can help us implement complicated business rules that may be difficult to express in the declarative form of SQL, but it then brings us to use the procedural (imperative) form of SQL, which is a general feature of SQL which is neither very friendly/expressive, programming-wise, and often less efficient performance-wise.

Maybe you can look into expressing the transformation and filtering desired in the context of a "plain" (declarative) SQL query.

一百个冬季 2024-08-18 12:26:47

使用光标。

在阅读文档时,可以将光标视为缓冲阅读器。如果您将每一行视为文档中的一行,那么您将读取下一行,执行操作,然后前进光标。

Use cursors.

A cursor can be thought of like a buffered reader, when reading through a document. If you think of each row as a line in a document, then you would read the next line, perform your operations, and then advance the cursor.

平定天下 2024-08-18 12:26:47

在存储过程中使用游标。
准备 SQL 查询

SELECT id FROM employee where department_id = 1;

创建游标,用于保存 SQL 查询返回的结果集。

DECLARE BonusDistributionCursor CURSOR FOR SELECT id FROM employee where department_id = 1;

要在从游标获取行时不返回任何结果时安全退出,请声明一个名为 NOT FOUND 的处理程序,并将值设置为已声明的变量。

DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;

在从游标获取下一行之前打开游标。

OPEN BonusDistributionCursor;

获取光标指向的下一行,然后将光标移动到下一行。

FETCH BonusDistributionCursor INTO employeeId;

根据所需的用例运行所需的业务逻辑。

DELIMITER $
CREATE PROCEDURE distributeYearlyBonus (IN departmentId VARCHAR(2))
BEGIN
DECLARE finished INTEGER DEFAULT 0;
DECLARE empId VARCHAR(TEXT) DEFAULT "";
DECLARE BonusDistributionCursor CURSOR FOR SELECT id FROM employee where department_id = departmentId;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN BonusDistributionCursor;
addBonus: LOOP
   FETCH BonusDistributionCursor INTO empId;
   IF finished = 1 THEN 
      LEAVE addBonus;
   END IF;
INSERT INTO `bonus_paid_details` (`employee_id`, `year`, `datetime`) VALUES (empId, YEAR(CURDATE());, now());
END LOOP addBonus;
CLOSE BonusDistributionCursor;
END$
DELIMITER ;

执行上面的脚本,你会发现创建了一个新的存储过程。

通过输入将接收奖金金额的部门 ID 来调用或调用存储过程。

CALL BonusDistributionCursor(1);

希望这能解释“如何使用存储过程中使用的游标进行迭代

Using a cursor within a stored procedure.
Prepare the SQL Query

SELECT id FROM employee where department_id = 1;

Create the cursor which will hold the result set returned by the SQL Query.

DECLARE BonusDistributionCursor CURSOR FOR SELECT id FROM employee where department_id = 1;

To have a safe exit when fetching a row from cursor does not return any result then declare a handler called NOT FOUND and set value to a declared variable

DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;

Open the Cursor before you can fetch the next row from the cursor.

OPEN BonusDistributionCursor;

Fetch the next row pointed by the cursor and move the cursor to next row after that.

FETCH BonusDistributionCursor INTO employeeId;

Run the desired business logic according to the usecase required.

DELIMITER $
CREATE PROCEDURE distributeYearlyBonus (IN departmentId VARCHAR(2))
BEGIN
DECLARE finished INTEGER DEFAULT 0;
DECLARE empId VARCHAR(TEXT) DEFAULT "";
DECLARE BonusDistributionCursor CURSOR FOR SELECT id FROM employee where department_id = departmentId;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN BonusDistributionCursor;
addBonus: LOOP
   FETCH BonusDistributionCursor INTO empId;
   IF finished = 1 THEN 
      LEAVE addBonus;
   END IF;
INSERT INTO `bonus_paid_details` (`employee_id`, `year`, `datetime`) VALUES (empId, YEAR(CURDATE());, now());
END LOOP addBonus;
CLOSE BonusDistributionCursor;
END$
DELIMITER ;

Execute the above script and you will find a new Stored Procedure created.

Call or Invoke the Stored Procedure by inputing the departmentId which will receive the bonus amount.

CALL BonusDistributionCursor(1);

Hope this explains "How to iterate using Cursors used within Stored Procedure"

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文