使用SQLAlchemy操作MSSQL时time_updated字段更新失效问题
问题背景
你使用SQLAlchemy 1.2.5 + pyodbc 4.0.22连接MS SQL Server 2014,最初定义了Persons表:
class Persons(Base): __tablename__ = 'Persons' ID_Person = Column(Integer(),primary_key = True) Affiliate_Name = Column(VARCHAR(200), unique=True, nullable = False) time_created = Column(DateTime(timezone=True), server_default=func.now()) time_updated = Column(DateTime(timezone=True), server_onupdate=func.now())
直接在SQL Server执行INSERT和UPDATE语句后,发现time_updated字段始终为NULL:
USE [TestDB] INSERT INTO [dbo].[Persons] (Affiliate_Name) values ('RANDOM NAME') UPDATE [dbo].[Persons] SET Affiliate_Name = 'RANDOM NAME2' WHERE Affiliate_Name = 'RANDOM NAME'
之后你修改了表定义,把time_updated改成:
time_updated = Column(DateTime(timezone=True), onupdate=datetime.now)
这时通过SQLAlchemy ORM更新数据时time_updated会正常更新,但直接在SQL Server里操作还是需要触发器才能生效。
问题原因
server_onupdate的局限性:SQLAlchemy的server_onupdate=func.now()本意是让数据库端自动处理字段更新,但MS SQL Server 2014本身并不支持像PostgreSQL那样的ON UPDATE CURRENT_TIMESTAMP内置列约束。SQLAlchemy无法为SQL Server生成对应的数据库端自动更新逻辑,所以直接执行SQL语句时不会触发time_updated的更新。onupdate的作用范围:onupdate=datetime.now是Python端的逻辑,只有当你通过SQLAlchemy ORM执行更新操作时,它才会自动填充当前时间并写入数据库。直接在数据库层面执行SQL语句时,这个Python端的逻辑不会被触发,所以字段不会更新。
解决方案:创建SQL Server触发器
如果想要不管是通过SQLAlchemy ORM还是直接执行SQL语句,都能自动更新time_updated字段,最可靠的方式是在SQL Server中为Persons表创建一个UPDATE触发器:
USE [TestDB] GO CREATE TRIGGER [dbo].[trg_Persons_UpdateTime] ON [dbo].[Persons] AFTER UPDATE AS BEGIN SET NOCOUNT ON; UPDATE p SET p.time_updated = GETUTCDATE() -- 若需要本地时间可替换为GETDATE() FROM [dbo].[Persons] p INNER JOIN inserted i ON p.ID_Person = i.ID_Person; END GO
这个触发器会在每次执行UPDATE操作后,自动将time_updated字段更新为当前UTC时间(或本地时间),不管更新操作是来自SQLAlchemy还是直接的SQL语句。
你也可以保留SQLAlchemy端的onupdate逻辑,两者不会冲突——只是触发器会最终覆盖Python端设置的时间,不过更推荐统一用触发器来保证数据库端的一致性。
内容的提问来源于stack exchange,提问作者chris dorn
相关产品推荐
相关产品推荐

