43

Does SQL Server allow constraint violations (i.e. deferred constraints) in a transaction as long as the transaction has not been committed yet?

I have a running, uncommitted transaction and while this transaction is running, I will change my data so that it will violate some constraints (like having duplicate primary keys for example). When I commit the transaction, the data will be in consistent, valid state. Is this generally allowed in SQL and specifically in MS SQL Server?

MicSim
  • 26,265
  • 16
  • 90
  • 133

5 Answers5

37

No, sorry. SQL Server does not allow deferred contraints in a transaction. It was present in SQL Server 6.5, but removed in SQL Server 2000:

SET DISABLE_DEF_CNST_CHK ON

Each individual statement must be consistent etc, regardless of whether it is in a transaction

Some RDBMS do allow this (e.g. Oracle, Postgres, Interbase)

Connect

There is a Microsoft Connect request, created in 2006, asking for this feature:

Option to defer foreign key constraint checking until transaction commit

There are various "chicken and egg" scenarios where it would be desirable to defer the checking of referential integrity constraints until commit time on a transaction.

Allow deferring of referential integrity constraint checking until commit time on a transaction (as an option). Suggest providing an option on BEGIN TRANSACTION that specifies this.

The last response from Microsoft came a decade ago:

Posted by Sameer [MSFT] on 10/13/2006 at 1:35 PM

Hello Greg,

Thanks for the feedback. We are aware of this and looking into it for a future release.

Sameer Verkhedkar
SQL Engine
[MSFT]

Which is Microsoft speak for "go away".

SQL-92 defines it

The feature was defined in July 1992 with SQL-92. An example syntax would be:

BEGIN TRANSACTION
   SET CONSTRAINTS ALL DEFERRED --applies only to the current transaction

   INSERT Customers ...
   INSERT Orders ...
   UPDATE Customers ... --add the thing we were missing

COMMIT TRANSACTION
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
gbn
  • 422,506
  • 82
  • 585
  • 676
  • 8
    to complete the picture: PostgreSQL allows deferred constraints as well. –  May 12 '11 at 08:58
  • 1
    And also Interbase allows this... However on the topic, with MSSQL Server I have the same problem with FK check in random synchronization functionality/feature (Periodically transfer some tables changes via custom API) ... and finally ended up with a list of the priority of the tables to insert/update/delete so I have the correct sequences. – Vasil Popov Jul 04 '14 at 12:12
  • I am unclear about this answer. Does the ' SET DISABLE_DEF_CNST_CHK ON' get you the desired behavior or not? If so, how could you run a command like this through C# calls? – Danny Ellis Jr. Sep 21 '17 at 16:36
  • 2
    @DannyEllisJr. It no longer exists, since SQL Server 2000 released 28 years ago... – gbn Sep 22 '17 at 09:13
1

You can disable your constraints while running your transaction, and then reenabling them when you are done.

ALTER TABLE mytable NOCHECK CONSTRAINT myconstraint

--... RUN TRANSACTION

ALTER TABLE mytable WITH CHECK CHECK CONTRAINT ALL

Warning: This will affect all connections.

Jens Mühlenhoff
  • 14,565
  • 6
  • 56
  • 113
Fredrik E
  • 1,840
  • 13
  • 18
  • 2
    Doesn't that affect all connections, rather than just your own, so another connection could violate a constraint whilst your transaction is in flight, and prevent commit from occurring? – Damien_The_Unbeliever May 12 '11 at 14:23
  • 1
    Yes. You could also lock the entire table to prevent this from happening. – Fredrik E Jun 01 '11 at 11:31
  • Nice. Have been looking for such a solution for a while. – David Catriel Sep 21 '11 at 15:37
  • 4
    The only issue with this, though, is that if my process crashes or ends abnormally, I can't guarantee that the constraints will be turned back on. The transaction will be rolled back by the server when it sees my connection was lost, but not resetting the constraint is a problem ... – David Catriel Sep 21 '11 at 18:56
  • 1
    That's not the only issue: if inconsistent modifications are committed by another transaction in the meantime, you're also in trouble. I'd suggested also that performance is likely to be poor. – Bart Read Jan 05 '16 at 15:14
  • 7
    This is a dangerous suggestion. While the UPDATEs you are running might only take a couple seconds, getting the affected constraints enabled again can take hours. So then you have a database that is essentially offline for OLTP (because you are -- hopefully -- holding an exclusive lock to that table) for hours, when all you wanted was to insert five rows into two tables. – cmenke Jan 17 '19 at 07:31
0

If you must (such as a Process to cleanse data from an import file), then put the intermediate data into Temp tables or table variables or staging tables and then only do the actions to the real tables with the constraints after you have cleansed it and made the data correct.

HLGEM
  • 94,695
  • 15
  • 113
  • 186
  • 1
    I downvoted because your suggestion only really applies to `CHECK` constraints - not `FOREIGN KEY` constraints which is where the real headaches lie (e.g. 1:1 relationships, self-referential tables, cyclical graph databases, polymorphic tables, subclasses in tables, etc). – Dai Jan 11 '21 at 15:19
0

SQL Server does not have deferred contraints option. But in some cases you can use Bulk Insert that supports ignoring constraints without problems of disabling. For more information you can see these links:

Then just before committing transaction, you will have to check the constraints on the whole table manually.

afruzan
  • 1,454
  • 19
  • 20
0

ALTERNATIVE you can use Merge commands. They resolve together, bypassing the constraint intermediary state. Here is an example. Before insert, John has pk id=1, Marta has pk id=2. Firstname and lastname are unique. The Merge command switch places and after the MERGE John has PK id=2 and Marta id=1. Switching Marta and Joe names are not possible with normal update commands.

CREATE TABLE dbo.People
(
    Id        int          NOT NULL IDENTITY,
    FirstName NVARCHAR(50) NOT NULL,
    LastName  NVARCHAR(50) NOT NULL,
    CONSTRAINT UQ_People UNIQUE (FirstName, LastName)
);

--SELECT * FROM People;
--TRUNCATE TABLE People;

INSERT INTO People (FirstName, LastName)
VALUES
    (N'John', N'Doe'),
    (N'Marta', N'Smith'),
    (N'Julius','Manfred');

MERGE dbo.People AS P
USING (
      SELECT 1 AS Id, 'Marta' AS FirstName, 'Smith' AS LastName
       UNION
      SELECT 2, 'John', 'Doe'
       UNION
      SELECT 8, 'Martin', 'Berg') AS Flip
ON P.Id = Flip.Id
WHEN MATCHED AND (P.FirstName <> Flip.FirstName OR P.LastName <> Flip.LastName) THEN
    UPDATE
       SET P.FirstName = Flip.FirstName,
           P.LastName  = Flip.LastName
WHEN NOT MATCHED BY TARGET THEN
    INSERT
    (FirstName,
     LastName)
    VALUES
        (Flip.FirstName,
         Flip.LastName)
WHEN NOT MATCHED BY SOURCE THEN DELETE
    OUTPUT INSERTED.*, DELETED.*, $action;

SELECT *
  FROM People;
Freddy
  • 75
  • 7