该列是从另一列计算出来的?


78

给出下表:

id | value
--------------
1     6
2     70

有没有一种方法可以添加基于同一表中另一列自动计算的列?就像一个VIEW,但是属于同一张表。例如,calculated将是的一半valueCalculated应该在value更改时自动更新,就像VIEW一样。

结果将是:

id | value | calculated
-----------------------
1     6       3
2     70      35

6
那么为什么不使用VIEW呢?
蒂姆·

2
持久(aka存储)的计算列通常更便宜,因为它们的存储方式与其他列一样。它们甚至可以被索引。
乔纳森·艾伦

1
非持久计算列只是一项便利功能。在处理ORM时,它通常比视图更好。
乔纳森·艾伦

Answers:


70

对于5.7.6及更高版本的MySql版本,Generated Column是一种很好的方法。

生成的列有两种:

  • 虚拟(默认)-从表中读取记录时,将即时计算列
  • 已存储-在表中写入/更新新记录时将计算列

两种类型都可以具有NOT NULL限制,但是只有一个存储的Generated Column可以成为索引的一部分。

对于当前情况,我们将使用存储的生成列。为实现这一点,我认为表中同时存在计算所需的两个值

CREATE TABLE order_details (price DOUBLE, quantity INT, amount DOUBLE AS (price * quantity));

INSERT INTO order_details (price, quantity) VALUES(100,1),(300,4),(60,8);

金额会自动在表格中弹出,您可以直接访问它,也请注意,每当您更新任何列时,金额也会被更新。


根据文档,这仅适用于NDB存储引擎,而不适用于InnoDB
悬崖ordheath

@cliffordheath-您错了。我使用mySQL 5.7,InnoDB引擎和Generated Column创建了示例sql小提琴-它可以正常工作:db-fiddle。您所引用的mySQL文档确实具有误导性,但假设它的内容应为:“从mySQL 5.7开始支持生成的列,从MySQL NDB Cluster 7.5.3开始支持NDB存储引擎”
user2988142

@ user2988142很高兴知道。即使您的措辞也可能含糊不清。也许提交文档错误报告?
悬崖峡湾

我在尝试通过JDBC与数据库交互时,尝试通过MySQL使用自动生成的列功能。-自动生成的单元格,实际上应该按照框架查询显示结果,该单元格是可编辑的,不会因自动生成而阻塞,并且会出错。让我知道,我需要对该代码进行哪些更改才能自动填充该行。另外,由于Mysql 5.7中引入了自动生成功能,但是我使用的是5.1 ver连接器/驱动程序。可能是个问题吗?
Ashish Ramtri

43

如果是选择,则可以按以下方式进行:

SELECT id, value, (value/2) AS calculated FROM mytable

另外,您还可以先更改表以添加缺少的列,然后执行UPDATE查询以计算新列的值,如下所示:

UPDATE mytable SET calculated = value/2;

如果必须是自动的,并且您的MySQL版本允许它,则可以尝试使用触发器


7
是的,这是一个SELECT,而不是向表中添加一列。
马修

4
您可以使用该calculated列在同一查询中运行进一步的计算吗?像(calculated* 2)ASdouble_calculated吗?
ZurabWeb 2014年

@ZurabWeb可以,但是在外部选择中(可使用嵌套选择语句)
Kamil Gosciminski


21

@krtek的答案是正确的方向,但是有两个问题。

坏消息是,在同一表的触发器中使用UPDATE无效。好消息是没有必要。还有一个新对象,您甚至可以在触摸桌子之前对其进行操作。

触发器变为:

CREATE TRIGGER halfcolumn_update BEFORE UPDATE ON my_table
  FOR EACH ROW BEGIN
    SET NEW.calculated = NEW.value/2;
  END;

另请注意BEGIN ... END; 语法必须使用有效的不同定界符进行解析。整个shebang变为:

DELIMITER |

CREATE TRIGGER halfcolumn_insert BEFORE INSERT ON my_table
  FOR EACH ROW BEGIN
    SET NEW.calculated = NEW.value/2;
  END;
|

CREATE TRIGGER halfcolumn_update BEFORE UPDATE ON my_table
  FOR EACH ROW BEGIN
    SET NEW.calculated = NEW.value/2;
  END;
|

DELIMITER ;

3
注意:在上面的代码中,用表名替换单词“ table”。我以为作者只是忘记了表名,而是在“ ON table”之后添加了该表名,因此浪费了几分钟。
Bloodboiler 2015年

好话@Bloodboiler-我修改了代码段,使其更加清晰。
杰里

1
请注意,这些年来,MySQL提出了一种更为优雅的方法来完成此任务。这个答案仍然有效,但是有一些反对使用触发器的好理由。如果使用Abhishek Gupta的答案对您而言很实际,那么您将获得一个更易于维护的系统。
杰里

9

您可以使用MYSQL 5.7中生成的列。

用法示例:

ALTER TABLE tbl_test
ADD COLUMN calc_val INT 
GENERATED ALWAYS AS (((`column1` - 1) * 16) + `column2`) STORED;

虚拟/存储

  • 虚拟:从表中读取记录时动态计算(默认)
  • 存储:在表中插入/更新新记录时计算

4

如果要向表中添加一列,该列会自动更新为其他某些列的一半,则可以使用触发器来执行此操作。

但是我认为已经提出的答案是实现此目的的更好方法。

干式编码触发器:

CREATE TRIGGER halfcolumn_insert AFTER INSERT ON table
  FOR EACH ROW BEGIN
    UPDATE table SET calculated = value / 2 WHERE id = NEW.id;
  END;
CREATE TRIGGER halfcolumn_update AFTER UPDATE ON table
  FOR EACH ROW BEGIN
    UPDATE table SET calculated = value / 2 WHERE id = NEW.id;
  END;

我认为您不能只触发一个触发器,因为我们必须响应的事件是不同的。


谢谢。在这种情况下,我们还应该考虑删除吗?
欧内斯特·汉

3

我希望这仍然可以帮助更多的人了解本文。如果需要计算列,为什么不只在视图中公开所需的列?不要只保存数据或使用触发器来使性能超负荷……只需在视图中公开您已经格式化/计算过的数据即可。

希望这可以帮助...


这取决于当OLAP解决方案的开销过多时,您可能希望添加具有四舍五入值的列以用于快速报告目的(因此,分组汇总总和等于CRM / ERP或银行帐户的金额)。在计算涉及的不只是微不足道的计算(例如销售利润率计算)的情况下,或者在需要合并其他表进行计算的情况下,存储值可以加快报告的速度。而是在插入所有数据时插入值,而不是在需要报告时将所有数据加在一起。
路易萨默斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.