Как использовать вспомогательные функции для создания хранимой процедуры, которая обновляет два поля mySQL - PullRequest
0 голосов
/ 23 октября 2019

Итак, у меня есть несколько таблиц на MySQL:

CREATE TABLE IF NOT EXISTS `salarygrade` (
  `GRADE` INT(11) NOT NULL,
  `HOURLYRATE` FLOAT NOT NULL,
  PRIMARY KEY (`GRADE`));

================================================================================

   CREATE TABLE IF NOT EXISTS `staffongrade` (
  `STAFFNO` INT(11) NOT NULL,
  `GRADE` INT(11) NOT NULL,
  `STARTDATE` DATE NULL DEFAULT NULL,
  `FINISHDATE` DATE NULL DEFAULT NULL,
  INDEX `STAFFONGRADE_FK` (`STAFFNO` ASC),
  INDEX `STAFFONGRADE2_FK` (`GRADE` ASC),
  PRIMARY KEY (`GRADE`, `STAFFNO`),
  CONSTRAINT `FK_STAFFONG_STAFFONGR_SALARYGR`
  FOREIGN KEY (`GRADE`)
  REFERENCES `salarygrade` (`GRADE`),
  CONSTRAINT `FK_STAFFONG_STAFFONGR_STAFF`
  FOREIGN KEY (`STAFFNO`)
  REFERENCES `staff` (`STAFFNO`));

================================================================================

CREATE TABLE IF NOT EXISTS `campaign` (
  `CAMPAIGN_NO` INT(11) NOT NULL,
  `TITLE` VARCHAR(30) NOT NULL,
  `CUSTOMER_ID` INT(11) NOT NULL,
  `THEME` VARCHAR(40) NULL DEFAULT NULL,
  `CAMPAIGNSTARTDATE` DATE NULL DEFAULT NULL,
  `CAMPAIGNFINISHDATE` DATE NULL DEFAULT NULL,
  `ESTIMATEDCOST` INT(11) NULL DEFAULT NULL,
  `ACTUALCOST` FLOAT NULL DEFAULT NULL,
  PRIMARY KEY (`CAMPAIGN_NO`),
  INDEX `OWNS_FK` (`CUSTOMER_ID` ASC),
  CONSTRAINT `FK_CAMPAIGN_OWNS_CUSTOMER`
    FOREIGN KEY (`CUSTOMER_ID`)
    REFERENCES `customer` (`CUSTOMER_ID`)
    ON DELETE RESTRICT
    ON UPDATE RESTRICT);

===========================================================================

CREATE TABLE IF NOT EXISTS `workson` (
  `STAFFNO` INT(11) NOT NULL,
  `CAMPAIGN_NO` INT(11) NOT NULL,
  `WDATE` DATE NOT NULL,
  `HOUR` FLOAT NULL DEFAULT NULL,

  PRIMARY KEY (`STAFFNO`, `CAMPAIGN_NO`, `WDATE`),
  INDEX `WORKSON_FK` (`STAFFNO` ASC),
  INDEX `FK_WORKSON_WORKSON2_CAMPAIGN_idx` (`CAMPAIGN_NO` ASC),
  CONSTRAINT `FK_WORKSON_WORKSON2_CAMPAIGN`
    FOREIGN KEY (`CAMPAIGN_NO`)
    REFERENCES `campaign` (`CAMPAIGN_NO`)
    ON DELETE RESTRICT
    ON UPDATE RESTRICT,
  CONSTRAINT `FK_WORKSON_WORKSON_STAFF`
    FOREIGN KEY (`STAFFNO`)
    REFERENCES `staff` (`STAFFNO`));

И я хочу создать хранимую процедуру с именем sp_finish_campaign (in c_title varchar(30)), которая принимает название кампании и завершает кампанию, обновляя CAMPAIGNFINISHDATE на текущую дату и ACTUALCOST на стоимость кампании, которая рассчитывается исходя из количества часов, отведенных на нее разными сотрудниками в разные даты, и уровня заработной платы (это изменяется в зависимости от идентификатора персонала и временных рамок на основеSTARTDATE и FINISHDATE таблицы staffongrade.

Для вычисления ACTUALCOST я создал вспомогательную функцию:

DELIMITER //

CREATE FUNCTION rate_on_date(staff_id int, given_date date)
    RETURNS int
    DETERMINISTIC
BEGIN
    DECLARE salaryGrade int;
    SET salaryGrade = (select grade from staffongrade
    where staffno = staff_id AND (given_date BETWEEN STARTDATE AND FINISHDATE));
RETURN salaryGrade;
END //
DELIMITER ;

, которая возвращает выплату grade на основедля параметров staff_id и given_date я даю:

select rate_on_date(1, "2018-02-02") as Grade_On_Date;

gradetable

Для параметраЯ полагаю, что мне придется получить его из таблицы workson, которая выглядит следующим образом:

workson

Я пытался использовать оператор выборачтобы получить выплату:

select hourlyrate as 'grade' from salarygrade where rate_on_date(1, "2018-02-02") = grade;

pay

Для расчета ACTUALCOST Я предполагаю, что мне нужно будет сделать расчет поумножить столбец HOUR на стоимость оценки и использовать столбцы WDATE и STAFFNO в таблице workson в качестве параметров для моей хранимой процедуры, которая рассчитает и обновит CAMPAIGNFINISHDATE и ACTUALCOST кампанииввод названия кампании в него. Но как мне это сделать?

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

1 Ответ

1 голос
/ 23 октября 2019

Вам не нужна функция. mysql может выполнять многостоловые обновления (см. https://dev.mysql.com/doc/refman/8.0/en/update.html), в вашем случае это может выглядеть следующим образом

update  campaign c
join 
(select c.campaign_no,
         sum(hour * hourlyrate) cost
from campaign c
join workson  w on w.campaign_no = c.campaign_no
join staffongrade s on s .staffno = w.staffno and w.wdate between s.startdate and s.finishdate
join salarygrade g on g.grade = s.grade
group by c.campaign_no
) s 
on s.campaign_no = c.campaign_no
set actualcost = s.cost
where c.campaign_no = 1
;

, где подзапрос делает необходимые

, если вы упрощаете ваши данныеэто должно быть легко доказать;

drop table if exists salarygrade,campaign,workson,staffongrade;

CREATE TABLE  `salarygrade` 
(  GRADE INT NOT NULL,
  hOURLYRATE decimal(10,2) NOT NULL
);
insert into salarygrade values(1,10),(2,20);

cREATE TABLE IF NOT EXISTS `staffongrade` (
  `STAFFNO` INT(11) NOT NULL,
  `GRADE` INT(11) NOT NULL,
  `STARTDATE` DATE NULL DEFAULT NULL,
  `FINISHDATE` DATE NULL DEFAULT NULL
  );

insert into staffongrade values
(1,1,'2019-01-01','2019-06-30'),(1,2,'2019-06-01','2019-12-31'),(2,1,'2019-01-01','2019-01-31');

CREATE TABLE IF NOT EXISTS `campaign` (
  `CAMPAIGN_NO` INT(11) NOT NULL,
  `CAMPAIGNSTARTDATE` DATE NULL DEFAULT NULL,
  `CAMPAIGNFINISHDATE` DATE NULL DEFAULT NULL,
  `ESTIMATEDCOST` INT(11) NULL DEFAULT NULL,
  `ACTUALCOST` FLOAT NULL DEFAULT NULL
  );

insert into campaign values (1,'2019-01-01','2019-12-31',null,null);

CREATE TABLE IF NOT EXISTS `workson` (
  `STAFFNO` INT(11) NOT NULL,
  `CAMPAIGN_NO` INT(11) NOT NULL,
  `WDATE` DATE NOT NULL,
  `HOUR` FLOAT NULL DEFAULT NULL
  );

insert into workson values
(1,1,'2019-01-01',1),(1,1,'2019-12-01',1),(2,1,'2019-01-01',1);

select * from campaign;
+-------------+-------------------+--------------------+---------------+------------+
| CAMPAIGN_NO | CAMPAIGNSTARTDATE | CAMPAIGNFINISHDATE | ESTIMATEDCOST | ACTUALCOST |
+-------------+-------------------+--------------------+---------------+------------+
|           1 | 2019-01-01        | 2019-12-31         |          NULL |         40 |
+-------------+-------------------+--------------------+---------------+------------+
1 row in set (0.00 sec)

Надо разбить, так что я оставлю вас, чтобы вы уронили обновление в процедуру.

ЕСЛИ staffongrade имеет NULL для конечной даты, а затем немного данныхтребуется очистка. для простоты я бы создал временную таблицу для заполнения пробелов и изменил бы оператор обновления, чтобы использовать дату окончания проекта (если это неизвестно, замените подходящую будущую дату). Этот код будет вставлен в вашу процедуру до обновления

so

insert into staffongrade values
(1,1,'2019-01-01',null),(1,2,'2019-07-01',null),(2,1,'2019-01-01',null);

drop temporary table if exists staffongradetemp;
create temporary table staffongradetemp like staffongrade;

insert into staffongradetemp 
select s.STAFFNO,s.GRADE,s.STARTDATE,
         case when s.FINISHDATE is not null then s.finishdate 
         else date_sub((select s1.startdate 
            from staffongrade s1 
             where s1.STAFFNO = s.STAFFNO and s1.startdate > s.STARTDATE 
             order by startdate limit 1), interval 1 day)
         end
from staffongrade s
;

select * from staffongradetemp;

+---------+-------+------------+------------+
| STAFFNO | GRADE | STARTDATE  | FINISHDATE |
+---------+-------+------------+------------+
|       1 |     1 | 2019-01-01 | 2019-06-30 |
|       1 |     2 | 2019-07-01 | NULL       |
|       2 |     1 | 2019-01-01 | NULL       |
+---------+-------+------------+------------+
3 rows in set (0.00 sec)

, который оставляет все последние finshdates равными NULL, которые мы можем отловить в операторе обновления, используя coalesce

update  campaign c
join 
(select c.campaign_no,
         sum(hour * hourlyrate) cost
from campaign c
join workson  w on w.campaign_no = c.campaign_no
join **staffongradetemp s** on s .staffno = w.staffno and w.wdate between s.startdate and **coalesce(s.finishdate,c.CAMPAIGNFINISHDATE)**
join salarygrade g on g.grade = s.grade
where c.campaign_no = 1
group by c.campaign_no
) s 
on s.campaign_no = c.campaign_no
set actualcost = s.cost
where 1 = 1;
...