As noted in this answer, we can turn off FK checking with
ALTER TABLE tableName NOCHECK CONSTRAINT ALL
and re-enable it with
ALTER TABLE tableName WITH CHECK CHECK CONSTRAINT ALL
Testing shows that this does not allow us to do TRUNCATE tableName
or DROP TABLE tableName
, presumably because the code for those operations simply checks whether an FK exists, and not whether it is currently being enforced. However, it does allow us to do DELETE FROM tableName
# fmt:off
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, ForeignKey
from sqlalchemy.exc import IntegrityError
# fmt:on
engine = create_engine("mssql+pyodbc://scott:tiger^5HHH@mssql_199")
meta = MetaData()
# example environment
parent = Table(
"parent",
meta,
Column("id", Integer, primary_key=True, autoincrement=False),
Column("name", String(50)),
)
child = Table(
"child",
meta,
Column("id", Integer, primary_key=True, autoincrement=False),
Column("name", String(50)),
Column("parent_id", Integer, ForeignKey("parent.id")),
)
meta.drop_all(engine, checkfirst=True)
meta.create_all(engine)
# example data
with engine.begin() as conn:
conn.execute(parent.insert(), dict(id=1, name="parent_1"))
conn.execute(child.insert(), dict(id=11, name="child_1", parent_id=1))
# try delete without disabling FK check
with engine.begin() as conn:
try:
conn.exec_driver_sql("DELETE FROM parent")
except IntegrityError as e:
print(e)
# (pyodbc.IntegrityError) ('23000', '[23000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]The DELETE statement conflicted with the REFERENCE constraint "FK__child__parent_id__7B5B524B". The conflict occurred in database "test", table "dbo.child", column \'parent_id\'. (547) (SQLExecDirectW); [23000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]The statement has been terminated. (3621)')
# try disabling FK check
with engine.begin() as conn:
conn.exec_driver_sql("ALTER TABLE child NOCHECK CONSTRAINT ALL")
conn.exec_driver_sql("DELETE FROM parent")
conn.exec_driver_sql(
"INSERT INTO parent (id, name) VALUES (1, 'new_parent_1')"
)
conn.exec_driver_sql("ALTER TABLE child WITH CHECK CHECK CONSTRAINT ALL")
# (no errors)
# verify results
with engine.begin() as conn:
print(conn.exec_driver_sql("SELECT * FROM parent").all())
# [(1, 'new_parent_1')]