您可能已经知道,SQLAlchemy Docs 中描述了您陷入困境的原因如下:
SQLAlchemy 默认使用OUTPUT INSERTED 来获取新生成的
通过IDENTITY 列或其他服务器端的主键值
默认值。 MS-SQL 不允许使用OUTPUT INSERTED on
有触发器的表。禁用OUTPUT INSERTED
在每个表的基础上,为每个表指定 implicit_returning=False
有触发器的表。
如果您将 SQLAlchemy 引擎设置为回显 SQL,您将看到默认情况下,它会执行以下操作:
INSERT INTO [table] (id, ...) OUTPUT inserted.[id] VALUES (...)
但如果你禁用implicit_returning,它会改为:
INSERT INTO [table] (id, ...) VALUES (...); select scope_identity()
所以问题是,“为以防万一,为所有表禁用 implicit_returning 有什么害处吗?”真的是,“用SCOPE_IDENTITY()代替OUTPUT INSERTED有什么缺点吗?”
我不是专家,但我的印象是虽然OUTPUT INSERTED 是这些天的首选方法,但SCOPE_IDENTITY() 通常也很好。过去,SQL Server 2008(可能还有更早的版本?)有一个错误,SCOPE_IDENTITY 有时没有返回正确的值,但我听说现在已经修复(see this question for more detail)。 (另一方面,@@IDENTITY 和 IDENT_CURRENT() 等其他技术仍然很危险,因为它们可能会在极端情况下返回错误的值。有关更多详细信息,请参阅同一页面上的 this answer 和其他技术。)
OUTPUT INSERTED 仍然具有的最大优势在于,它可以适用于通过单个 INSERT 语句插入多行的情况。这是你用 SQLAlchemy 做的事情吗?应该不会吧?所以没关系。
请注意,如果您必须为许多表禁用 implicit_returning,您可以通过为它创建一个 mixin(以及您希望所有表继承的任何其他列和属性)来避免一些样板:
class AutoincTriggerMixin():
__table_args__ = {
'implicit_returning': False
}
id = Column(Integer, primary_key=True, autoincrement=True)
class SomeModel(AutoincTriggerMixin, Base):
some_column = Column(String(1000))
...
有关详细信息,请参阅this page in the SQLALchemy documentation。作为一个额外的好处,它使哪些表涉及触发器更加明显。