我有一个系统时态 SQL Server 表,为行开始日期生成的日期似乎不正确。
我有一个 Flask + SQLAlchemy REST API 将数据插入到临时表中。我的表中有两个日期 - 第一个是应用程序生成的;第二个是应用程序生成的。另一个是将记录写入数据库时创建的行开始日期。即使我在两个步骤之间放置
time.sleep(1)
,应用程序生成的日期也会晚于系统临时日期。
为什么应用程序生成的日期并不总是早于系统生成的日期?
下面是一个例子。
型号:
class TestModel(db.Model):
__tablename__ = "testtbl"
id = db.Column(db.Integer, primary_key=True)
asofdts = db.Column(DATETIME2, nullable=False)
@classmethod
def add_system_versioning(
cls,
row_eff_dts="row_eff_dts",
row_exp_dts="row_exp_dts",
*args,
**kwargs,
):
"""
Enable system versioning on this table. First, add the row eff/exp dates. Then, add a history table.
"""
HISTORY_TABLE_NAME = f"dbo.{cls.__tablename__}_history"
sql = f"""
ALTER TABLE {cls.__tablename__}
ADD
{row_eff_dts} DATETIME2 GENERATED ALWAYS AS ROW START NOT NULL DEFAULT GETUTCDATE(),
{row_exp_dts} DATETIME2 GENERATED ALWAYS AS ROW END NOT NULL DEFAULT CONVERT(DATETIME2, '9999-12-31 00:00:00.0000000'),
PERIOD FOR SYSTEM_TIME ({row_eff_dts}, {row_exp_dts})
"""
try:
db.session.execute(text(sql))
db.session.commit()
except Exception:
db.session.rollback()
sql = f"""
ALTER TABLE {cls.__tablename__}
SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE={HISTORY_TABLE_NAME}))
"""
try:
db.session.execute(text(sql))
db.session.commit()
except Exception:
db.session.rollback()
API资源:
class TestResource(Resource):
def get(self, *args, **kwargs):
# setup the table
db.create_all()
TestModel.add_system_versioning()
return {"hello": "world"}
def post(self, *args, **kwargs):
# get the current UTC datetime from the database (which is what the row_eff_dts should use too!)
asofdts = db.session.execute(
text("SELECT CAST(GETUTCDATE() AS DATETIME2)")
).scalar()
time.sleep(1)
# create a new row
obj = TestModel(asofdts=asofdts)
db.session.add(obj)
db.session.commit()
return {"hello": "world"}
结果:
请注意,
row_eff_dts
位于 asofdts
列之前 5 秒或更长时间。
一切都在本地运行。 SQL Server 也在本地的 Docker 容器中运行。
多亏了 Charlieface,我找到了解决方案。原始代码在单个事务中运行,行开始日期是事务的开始。如果我修改代码以在单独的事务中获取
asofdts
,那么一切都会按预期进行。
def post(self, *args, **kwargs):
# get the current UTC datetime from the database (which is what the row_eff_dts should use too!)
with db.session.begin():
asofdts, transaction_id = db.session.execute(
text(
"SELECT CAST(GETUTCDATE() AS DATETIME2) AS asofdts, CURRENT_TRANSACTION_ID() AS transaction_id"
)
).first()
print(transaction_id)
_, transaction_id = db.session.execute(
text(
"SELECT CAST(GETUTCDATE() AS DATETIME2) AS asofdts, CURRENT_TRANSACTION_ID() AS transaction_id"
)
).first()
print(transaction_id) # this is a new transaction
# create a new row
obj = TestModel(asofdts=asofdts)
db.session.add(obj)
db.session.commit()
return {"hello": "world"}
新结果: