MySQL 使用一个 Insert 语句而不是游标循环
MySQL use one Insert statement instead of cursor loop
我有这个触发器,它运行良好,但我读到使用游标和循环对性能不利。
那么我怎样才能用一个插入语句代替使用游标和循环,就像更新部分中的那个语句:
CREATE TRIGGER `after_insert_invoice` AFTER INSERT ON `invoicetbl`
FOR EACH ROW BEGIN
DECLARE v_finished INTEGER DEFAULT 0;
DECLARE my_hour INT(11) DEFAULT 0;
DECLARE my_day INT(11) DEFAULT 0;
DECLARE my_month INT(11) DEFAULT 0;
DECLARE current_item_id BINARY(16);
DECLARE current_item_total_price DECIMAL(11, 2);
DECLARE current_item_count DECIMAL(11, 3);
DECLARE cur CURSOR FOR SELECT item_id, item_total_price, item_count
FROM invoice_itemtbl
WHERE invoice_id = NEW.invoice_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_finished = 1;
SET my_month = EXTRACT(YEAR_MONTH FROM NEW.invoice_date);
SET my_day = CONCAT(my_month, LPAD(DAY(NEW.invoice_date), 2, '0'));
SET my_hour = CONCAT(my_day, LPAD(HOUR(NEW.invoice_date), 2, '0'));
IF NEW.invoice_type = 0 THEN
OPEN cur;
start_loop: LOOP
FETCH cur INTO current_item_id,
current_item_total_price,
current_item_count;
IF v_finished = 1 THEN
LEAVE start_loop;
ELSE
(续...)
INSERT
INTO sales_result_company_item_month(company_id, currency_id,
time_value, item_id, result, invoices_count, item_qty )
VALUES (NEW.company_id, NEW.currency_id, my_month, current_item_id,
current_item_total_price, 1, current_item_count )
ON DUPLICATE KEY
UPDATE result = result + current_item_total_price,
invoices_count = invoices_count + 1,
item_qty = item_qty + current_item_count;
END IF;
END LOOP;
CLOSE cur;
END IF;
IF NEW.invoice_type = 1 THEN
//like here:
UPDATE sales_result_company_item_month m
INNER JOIN
(
SELECT item_id, returns_count, returns_total
FROM invoice_returns
WHERE return_invoice_id = NEW.invoice_id
) f ON m.item_id = f.item_id SET m.result = m.result - f.returns_total,
m.item_qty = m.item_qty - f.returns_count
WHERE m.company_id = NEW.company_id
AND m.currency_id = NEW.currency_id
AND m.time_value = my_month;
END IF;
END
(从评论中复制——在 'fixing' 问题之后,包括 Cursor 的消除):
INSERT
INTO sales_result_company_item_month
(company_id, currency_id, time_value, item_id,
result, invoices_count, item_qty)
SELECT NEW.company_id, NEW.currency_id, my_month, t.item_id,
t.item_total_price, 1, t.item_count
FROM invoice_itemtbl t
WHERE invoice_id = NEW.invoice_id
ON DUPLICATE KEY UPDATE
result = result + t.item_total_price,
invoices_count = invoices_count + 1,
item_qty = item_qty + t.item_count;
是的,您或许可以去掉 Cursor。
可以做到
INSERT INTO t ( ... )
ON DUPLICATE KEY UPDATE ...
SELECT ...;
其中,SELECT
将所有行提取到 "upsert"。您可能需要 UPDATE
子句中的 VALUES()
来区分所选列和 table 中已有的列。请参阅手册中的示例。
如果您想进一步讨论这个问题,请
- 为相关的table提供
SHOW CREATE TABLE
,
- 摆脱 kruft(例如,my_day,未使用)
- 用
_
前缀局部变量,或以其他方式将它们与 table 列区分开来。
current_item_count DECIMAL(11, 3)
不解:什么"count"需要3位小数?
我有这个触发器,它运行良好,但我读到使用游标和循环对性能不利。
那么我怎样才能用一个插入语句代替使用游标和循环,就像更新部分中的那个语句:
CREATE TRIGGER `after_insert_invoice` AFTER INSERT ON `invoicetbl`
FOR EACH ROW BEGIN
DECLARE v_finished INTEGER DEFAULT 0;
DECLARE my_hour INT(11) DEFAULT 0;
DECLARE my_day INT(11) DEFAULT 0;
DECLARE my_month INT(11) DEFAULT 0;
DECLARE current_item_id BINARY(16);
DECLARE current_item_total_price DECIMAL(11, 2);
DECLARE current_item_count DECIMAL(11, 3);
DECLARE cur CURSOR FOR SELECT item_id, item_total_price, item_count
FROM invoice_itemtbl
WHERE invoice_id = NEW.invoice_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_finished = 1;
SET my_month = EXTRACT(YEAR_MONTH FROM NEW.invoice_date);
SET my_day = CONCAT(my_month, LPAD(DAY(NEW.invoice_date), 2, '0'));
SET my_hour = CONCAT(my_day, LPAD(HOUR(NEW.invoice_date), 2, '0'));
IF NEW.invoice_type = 0 THEN
OPEN cur;
start_loop: LOOP
FETCH cur INTO current_item_id,
current_item_total_price,
current_item_count;
IF v_finished = 1 THEN
LEAVE start_loop;
ELSE
(续...)
INSERT
INTO sales_result_company_item_month(company_id, currency_id,
time_value, item_id, result, invoices_count, item_qty )
VALUES (NEW.company_id, NEW.currency_id, my_month, current_item_id,
current_item_total_price, 1, current_item_count )
ON DUPLICATE KEY
UPDATE result = result + current_item_total_price,
invoices_count = invoices_count + 1,
item_qty = item_qty + current_item_count;
END IF;
END LOOP;
CLOSE cur;
END IF;
IF NEW.invoice_type = 1 THEN
//like here:
UPDATE sales_result_company_item_month m
INNER JOIN
(
SELECT item_id, returns_count, returns_total
FROM invoice_returns
WHERE return_invoice_id = NEW.invoice_id
) f ON m.item_id = f.item_id SET m.result = m.result - f.returns_total,
m.item_qty = m.item_qty - f.returns_count
WHERE m.company_id = NEW.company_id
AND m.currency_id = NEW.currency_id
AND m.time_value = my_month;
END IF;
END
(从评论中复制——在 'fixing' 问题之后,包括 Cursor 的消除):
INSERT
INTO sales_result_company_item_month
(company_id, currency_id, time_value, item_id,
result, invoices_count, item_qty)
SELECT NEW.company_id, NEW.currency_id, my_month, t.item_id,
t.item_total_price, 1, t.item_count
FROM invoice_itemtbl t
WHERE invoice_id = NEW.invoice_id
ON DUPLICATE KEY UPDATE
result = result + t.item_total_price,
invoices_count = invoices_count + 1,
item_qty = item_qty + t.item_count;
是的,您或许可以去掉 Cursor。
可以做到
INSERT INTO t ( ... )
ON DUPLICATE KEY UPDATE ...
SELECT ...;
其中,SELECT
将所有行提取到 "upsert"。您可能需要 UPDATE
子句中的 VALUES()
来区分所选列和 table 中已有的列。请参阅手册中的示例。
如果您想进一步讨论这个问题,请
- 为相关的table提供
SHOW CREATE TABLE
, - 摆脱 kruft(例如,my_day,未使用)
- 用
_
前缀局部变量,或以其他方式将它们与 table 列区分开来。
current_item_count DECIMAL(11, 3)
不解:什么"count"需要3位小数?