编程数据工程师

如何实现SQL中计算列的自动生成和更新?它们在表级声明与查询计算之间有什么区别?可能会出现什么困难?

用 Hintsage AI 助手通过面试

答案。

计算列是指基于表中其他列的值自动生成的列。该概念的提出是为了简化对典型计算结果的访问,从而避免在每个查询中重复公式。它们可以被声明为虚拟(值在每次选择时计算)或物理(在数据更新时物理保存)。

问题在于,并不是所有的表达式都能在表级计算(函数类型的限制),而物理存储衍生数据会占用空间并需要在更新时保持其时效性。

解决方案:在可能的情况下,在DDL级别声明计算列,或通过触发器/过程实现更新逻辑,如果需要存储或使用聚合。

代码示例(MS SQL Server):

ALTER TABLE orders ADD total_sum AS (quantity * price); -- 虚拟 ALTER TABLE orders ADD total_sum_persisted AS (quantity * price) PERSISTED; -- 物理

关键特点:

  • 虚拟列减少冗余,但降低查询速度。
  • 物化列会自动更新,但会消耗空间和资源。
  • 一些表达式是禁止的:在计算列中不能使用不可复制的函数或子查询。

有陷阱的问题。

可以在计算列上建立索引吗?

如果列声明为PERSISTED(物理保存),可以建立索引。对于虚拟列——并不总是如此,具体取决于数据库管理系统。

CREATE INDEX ix_total_sum ON orders (total_sum_persisted);

如果源列发生变化,计算列会自动更新吗?

是的,在插入/编辑源数据时,如果列在表级声明为计算列,会自动计算新的值。

如果计算列引用NULL,会发生什么?

大多数数据库管理系统在至少一个操作数为NULL时返回NULL——务必通过COALESCE/ISNULL处理可能的NULL值。

ALTER TABLE orders ADD total_with_discount AS (COALESCE(quantity * price, 0));

常见错误和反模式

  • 在没有必要的情况下存储重复的衍生值,这会导致体积增加。
  • 使用复杂的、不可索引的表达式,导致性能下降。
  • 如果计算在应用程序级别而不是数据库级别进行不一致更新。

生活中的例子

消极案例

数据库中分别存储商品的总和和价格,而总值单独存储。在更新商品时,忘记更新总值。

优点:

  • 快速读取

缺点:

  • 数据不一致,重新计算时困难。

积极案例

最终总和被声明为虚拟列。始终准确,与源值不冲突。

优点:

  • 没有不同步
  • 简化了与数据库的代码工作

缺点:

  • 在复杂公式情况下略有延迟,如果计算非常多。