【问题标题】:Stored Procedures and Cursors in MySqlMySql 中的存储过程和游标
【发布时间】:2014-04-29 12:19:03
【问题描述】:

我有两张表,Employee 和 Department。它们看起来像这样:

Employee
Name
SSN
DeptID
Salary

Department
DName
DId
Total_Sal

Department.total_sal 是属于该部门的所有员工工资的总和。我需要一个存储过程,它利用游标遍历每个员工,并更新相应部门的工资。我以前从未使用过游标或存储过程,我对如何遍历一个表但更新另一个表有点困惑。任何帮助/建议表示赞赏。

另一个简单的问题,我喜欢在 SQLFIDDLE 中完成我所有的 sql 工作,有人知道它是否也支持存储过程/游标吗?

这是我的第一次尝试,我想在开头也擦除 Department.Total_sal 是个好主意?

DELIMITER //
DROP PROCEDURE IF EXISTS cur_sal

CREATE PROCEDURE cur_sal
  BEGIN
    DECLARE e_sal, e_dno INT;
    DECLARE d_sal, d_dno INT;
    DECLARE cur_emp CURSOR FOR SELECT salary, DeptId FROM employee;
    DECLARE cur_dep CURSOR FOR SELECT DId, Total_sal FROM department;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

    OPEN cur_emp;
    OPEN cur_dep;


    r_loop: LOOP
      FETCH cur_emp INTO e_sal, e_dno;
      FETCH cur_dep INTO d_sal, d_dno;
      IF done THEN
        LEAVE r_loop;
      END IF;

      IF e_dno = d_dno THEN
        UPDATE department SET total_sal = total_sal + e_sal;
      END IF;
    END LOOP;

    CLOSE cur_emp;
    CLOSE cur_dep;
  END //

【问题讨论】:

  • 你需要先展示你尝试过的东西
  • 为什么要用光标进行迭代?带有连接的单个更新查询可以满足您的需求。

标签: mysql


【解决方案1】:

无需使用游标来执行此操作。有多种方法可以实现相同的结果,包括连接和子选择。在下面的代码中,我使用了子选择(因为我必须回去工作而且很快):

将其放入构建方案部分:

-- create the tables
CREATE TABLE Employee 
    (
    Name varchar(50),
    SSN varchar(50),
    DeptID int,
    Salary float
    );

CREATE TABLE Department 
    (
    DName varchar(50),
    DId int,
    Total_Sal float
    );

-- insert default values
insert into Employee (name,ssn,deptid,salary)
values('Mr Test', '12345a', 1,10000);

insert into Employee (name,ssn,deptid,salary)
values('Mr Tester', '12345b', 1,33000);

-- notice the total_sal is 63000
insert into department (DName, DID, total_sal)
values('Test department',1,63000);

-- now we update the total_sal to be the sum of everyone in that department
update Department 
set total_sal = (SELECT SUM(Salary) FROM employee where employee.DeptID = Department.DID)

在可执行部分运行:

select * from department

您会注意到,total_sal 现在是 43000,而不是插入的初始值 63000。

我使用的更新语句将遍历每个部门。

这里是 SQLFiddle:SQLFiddle

如果你坚持使用光标,你可以这样做:

CREATE PROCEDURE UpdateSalaries()
BEGIN

  -- Cursor Example
  declare DeptID INTEGER;
  declare TotalSalary FLOAT;

  declare SalaryCursor Cursor for select DId, 
    (SELECT SUM(Salary) FROM employee where employee.DeptID = Department.DID) from department

    DECLARE CONTINUE HANDLER 
    FOR NOT FOUND SET finished = 1;

    open SalaryCursor
      FETCH SalaryCursor INTO DeptID, TotalSalary;

    get_Salary: LOOP

      update Department 
      set total_sal = TotalSalary
      WHERE DID = DeptID

    END LOOP get_Salary;

  CLOSE SalaryCursor;
END

【讨论】:

  • PaulG,谢谢您的建议。我知道这里有更容易和更有效的方法来实现目标。然而,我的任务是专门使用存储过程/游标来完成。
  • 你可以把它放在一个存储过程中,但你仍然不需要游标lol
  • @Hituptony 是的,我知道游标效率很低,但是我在这个作业中没有选择。
【解决方案2】:
CREATE PROCEDURE cur_sal
  BEGIN
    DECLARE e_sal, e_dno INT;
    DECLARE sum_sal INT
    DECLARE d_sal, d_dno INT;
    DECLARE cur_emp CURSOR FOR SELECT salary, Dno FROM employee;
    DECLARE cur_dep CURSOR FOR SELECT Dno, Total_sal FROM department;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

    OPEN cur_emp;
    OPEN cur_dep;


    r_loop: LOOP
      FETCH cur_emp INTO e_sal, e_dno;
      FETCH cur_dep INTO d_sal, d_dno;
      IF done THEN
        LEAVE r_loop;
      END IF;

      IF e_dno = d_dno THEN
         SET sum_sal = sum_sal + e_sal;
      END IF;
    END LOOP;
UPDATE department SET total_sal = sum_sal where DId = Dno;
    CLOSE cur_emp;
    CLOSE cur_dep;
  END //

我想你已经很接近了,你想使用一个临时变量来保存所有事务的总和,直到你到达循环的末尾。当你到最后你用你的总和值更新部门薪水。我刚刚使用了 sum_sal。

更新语句需要一个 where 子句,你基本上会让你的光标充满你的值

【讨论】:

  • 感谢您使用临时 sum 变量的建议,这是个好主意。不过,我对您的解决方案有疑问,它将如何处理不同的部门?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-14
  • 2018-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多