tb_sites_21
我正在创建触发器
CREATE DEFINER=`root`@`localhost`
TRIGGER `color_changed`
AFTER INSERT ON `tb_tickets`
FOR EACH ROW UPDATE tb_sites_21
SET color_status = NEW.status
WHERE site_id = NEW.site_id;
它工作正常,我只需要tb_sites_21,我希望这21可以从为其创建新条目的tb_tickets的program_id中选择。
类似这样的东西:
CREATE DEFINER=`root`@`localhost`
TRIGGER `color_changed`
AFTER INSERT ON `tb_tickets`
FOR EACH ROW UPDATE tb_sites_NEW.program_id
SET color_status = NEW.status
WHERE site_id = NEW.site_id;
或
CREATE DEFINER=root@localhost
TRIGGER color_changed AFTER INSERT ON tb_tickets FOR EACH ROW
SET @table_name := (SELECT CONCAT("tb_sites_" , program_id)
FROM tb_tickets
WHERE ticket_id = NEW.ticket_id);
UPDATE table_name set
color_status = NEW.status WHERE site_id = NEW.site_id
我该如何实现?
答案 0 :(得分:3)
每个program_id的表分区是一个棘手的设计:SELECT * FROM sales + @yymm
您不能在触发器内部动态SQL / prepared语句,因此唯一的vialble选项使用IF
语句,但是您需要提前知道表名。
CREATE DEFINER=`root`@`localhost`
TRIGGER `color_changed`
AFTER INSERT
BEGIN
IF NEW.program_id = 1 THEN
UPDATE tb_sites_1
SET color_status = NEW.status
WHERE site_id = NEW.site_id;
ELSEIF NEW.program_id = 2 THEN
UPDATE tb_sites_2
SET color_status = NEW.status
WHERE site_id = NEW.site_id;
ELSEIF ... THEN
-- ...
ELSE
-- ...
END
END;
答案 1 :(得分:3)
与论坛上的许多问题一样,我将尝试退后一步,以了解真正的问题是什么。然后解决它。
一个明显的问题是您决定使用TRIGGER
,但是由于两件事而碰壁:(1)模式具有多个相同的表,并且(2)触发器不允许“准备”。
备份, real 问题是基于“ program_id”存储一些数据。
因此,如果我们退出使用触发器(2),则可以使用存储过程和/或应用程序代码来完成任务。伴随着我经常说的话:“触发器(和外键)有局限性;您不一定要对它们做任何想做的事。”
但是,我认为(1)是更严重的设计缺陷。具有多个相同的表几乎总是“不正确的架构设计”,它们之间的区别仅在于表名。从长远来看,通常在单个表中有一个额外的列(program_id
)很容易,而且从长远来看更有效。
我了解“单表”方法也有例外。如果您认为必须有单独的表格,请详细说明,以便我们进一步讨论。
答案 2 :(得分:2)
表名不能使用变量。无论您走到哪里,这都是关于SQL的事实,在解析查询时必须固定查询以及查询中引用的所有表和列。对于触发器,这意味着表名称必须在您CREATE TRIGGER
时固定。
替代方法是将动态SQL与PREPARE and EXECUTE一起使用。这使您可以从字符串表达式构建查询,并使MySQL解析并在运行时执行它。
它可能看起来像这样:
SELECT CONCAT("tb_sites_" , program_id) INTO @table_name
FROM tb_tickets WHERE ticket_id = NEW.ticket_id;
SET @sql = CONCAT('UPDATE ', @table_name,
' SET color_status = ? WHERE site_id = ?');
SET @color_status = NEW.status, @site_id = NEW.site_id;
PREPARE stmt FROM @sql;
EXECUTE stmt USING @color_status, @site_id;
DEALLOCATE PREPARE stmt;
https://dev.mysql.com/doc/refman/8.0/en/sql-syntax-prepared-statements.html说:
已准备好的语句的SQL语法可以在存储过程中使用,但不能在存储函数或触发器中使用。
P.Salmon和Lukasz Szozda的其他评论和答案一直在试图解释这一点,但是您似乎没有在听。
共有三种选择:
1。对每种情况进行硬编码(Lukasz Szozda的答案)
我会使用CASE
语句代替IF / THEN / ELSE IF块链,但是逻辑是相同的。您需要
CREATE DEFINER=`root`@`localhost`
TRIGGER `color_changed`
AFTER INSERT
BEGIN
CASE NEW.program_id
WHEN 1 THEN
UPDATE tb_sites_1
SET color_status = NEW.status
WHERE site_id = NEW.site_id;
WHEN 2 THEN
UPDATE tb_sites_2
SET color_status = NEW.status
WHERE site_id = NEW.site_id;
WHEN 3 THEN
UPDATE tb_sites_3
SET color_status = NEW.status
WHERE site_id = NEW.site_id;
...etc...
END
END
这有一个缺点,如果要更新的表很多,它可能会很长,并且每次添加新表时都需要重新定义触发器。
2。使用存储过程而不是触发器(Rick James的答案)
这不使用触发器。相反,它运行两个语句,一个INSERT,然后是相应站点表的UPDATE。您可以使用PREPARE / EXECUTE语法在存储过程中执行此操作。
CREATE PROCEDURE InsertTicket(
IN in_ticket_id INT,
IN in_program_id INT,
IN in_color_status VARCHAR(10),
IN in_site_id INT)
BEGIN
DECLARE table_name VARCHAR(64);
-- First insert into the tickets table
INSERT INTO tb_tickets
SET ticket_id = in_ticket_id,
program_id = in_program_id,
color_status = in_color_status,
site_id = in_site_id;
-- Second, do a dynamic update into the respective site table
SET table_name = CONCAT('tb_sites_', in_program_id);
SET @sql = CONCAT('UPDATE ', table_name,
' SET color_status = ? WHERE site_id = ?');
SET @color_status = in_color_status, @site_id = in_site_id;
PREPARE stmt FROM @sql;
EXECUTE stmt USING @color_status, @site_id;
DEALLOCATE PREPARE stmt;
END
您还可以使用任何应用程序编码语言编写一对等效的语句。您不需要执行存储过程。
您可以使用事务包装这两个语句,以便将它们同时提交,否则都回滚。
此替代方法将需要您更改应用程序插入票证的方式。因此,您必须更改应用程序代码。如果您没有更改应用程序代码的权限,那么这不是一个很好的选择。
3。将sites表重构为一个表(Paul Spiegel的评论)
有几个人建议这样做,但是您说您无权更改表格设计。这是不幸的,但很常见。更改应用程序中的表设计非常昂贵,因为取决于当前的表设计,可能会有很多应用程序代码。需要重构所有代码以支持对表的更改。