{"id":276,"date":"2007-06-22T00:00:00","date_gmt":"2007-06-22T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/foreign-keys-and-their-states\/"},"modified":"2021-08-24T13:40:43","modified_gmt":"2021-08-24T13:40:43","slug":"foreign-keys-and-their-states","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/foreign-keys-and-their-states\/","title":{"rendered":"Foreign Keys and their States"},"content":{"rendered":"<p>Foreign keys enforce referential integrity and in this way contribute to the database&#8217;s consistency. However, sometimes it is necessary to disable them temporarily. I have seen some confusion as to how&#160;one re-enables these disabled constraints in such a way that the database&#8217;s consistency remains unaffected. So, in this article, I will examine some rarely explored areas that concern foreign keys; in particular, I will look at disabled and un-trusted foreign keys.<\/p>\n<h3>Why would one ever disable a foreign key? <\/h3>\n<p>An example for the need to temporarily disable foreign keys is when one wants to load a large batch of consistent data into a set of tables that reference each other via foreign keys. It is easier to load data on a per table basis, and if the foreign keys are disabled, one can load the table data for each table. For example if there is a foreign key set up on a table <span class=\"CodeInText\"><strong>referencingTable<\/strong><\/span> and a <span class=\"CodeInText\"><strong>referencedTable<\/strong><\/span>, one should start loading data for the <span class=\"CodeInText\"><strong>referencedTable<\/strong><\/span> first. But foreign key relationships can be complex, and sometimes even the order in which data rows are added to a single table is significant. And, if we have millions of rows of data, and we are confident that they are consistent, disabling foreign keys is the best option.<\/p>\n<p>After the large data batch is loaded, the database should once again enforce its consistency rules, and the foreign keys should be restored to their original states.<\/p>\n<p>I will use a simple two table example to demonstrate how to enable and disable foreign keys, and how to detect inconsistencies in the database. The two tables are <strong>tableReferenced<\/strong> and <strong>tableReferencing<\/strong>. As the name suggests the <strong>tableReferencing <\/strong>will reference the <strong>tableReferenced<\/strong> table.<\/p>\n<p>To create and populate the two tables run:<\/p>\n<p><code>CREATE&#160;TABLE&#160;tableReferenced&#160; <br \/>&#160;&#160;&#160;(&#160;colA&#160;INT&#160;NOT&#160;NULL&#160;PRIMARY&#160;KEY&#160;IDENTITY(1,&#160;1) <br \/>&#160;&#160;&#160;&#160;,&#160;colB&#160;NVARCHAR(20)&#160;DEFAULT&#160;N'some&#160;data' <br \/>&#160;&#160;&#160;&#160;) <br \/>GO <\/p>\n<p>INSERT&#160;INTO&#160;tableReferenced&#160;DEFAULT&#160;VALUES <br \/>INSERT&#160;INTO&#160;tableReferenced&#160;DEFAULT&#160;VALUES <br \/>GO <\/p>\n<p>CREATE&#160;TABLE&#160;tableReferencing <br \/>&#160;&#160;&#160;&#160;( <br \/>&#160;&#160;&#160;&#160;&#160;&#160;colC&#160;INT&#160;NOT&#160;NULL <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;PRIMARY&#160;KEY <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;IDENTITY(2,&#160;2), <br \/>&#160;&#160;&#160;&#160;&#160;&#160;colARef&#160;INT&#160;NOT&#160;NULL <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;CONSTRAINT&#160;FK_References&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;REFERENCES&#160;tableReferenced&#160;(&#160;colA&#160;)&#160; <br \/>&#160;&#160;&#160;&#160;) <br \/>GO <\/p>\n<p>INSERT&#160;INTO&#160;tableReferencing&#160;VALUES&#160;&#160;(&#160;1&#160;) <br \/>INSERT&#160;INTO&#160;tableReferencing&#160;VALUES&#160;&#160;(&#160;1&#160;) <br \/>INSERT&#160;INTO&#160;tableReferencing&#160;VALUES&#160;&#160;(&#160;2&#160;)&#160;<\/p>\n<p>GO <br \/><\/code><\/p>\n<p>This will create the two tables and populates them like this:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/404-andras1.jpg\" alt=\"404-andras1.jpg\" \/><\/p>\n<p>The foreign key is&#160;established, and one can check this by inserting a row that would violate referential integrity:<\/p>\n<p><code>INSERT&#160;INTO&#160;tableReferencing&#160;VALUES&#160;(&#160;1000&#160;)<\/code><\/p>\n<p>This insert should fail, because there is no corresponding row in <strong><span class=\"CodeInText\">tableReferenced<\/span><\/strong>. SQL Server, as expected, reacts with the following error message:<\/p>\n<p><code>Msg&#160;547,&#160;Level&#160;16,&#160;State&#160;0,&#160;Line&#160;1 <\/p>\n<p>The&#160;INSERT&#160;statement&#160;conflicted&#160;with&#160;the&#160;FOREIGN&#160;KEY&#160;constraint<br \/>\"FK_References\". The&#160;conflict&#160;occurred&#160;in&#160;database&#160;\"FKplay\",<br \/>table&#160;\"dbo.tableReferenced\", column&#160;'colA'. <\/p>\n<p>The&#160;statement&#160;has&#160;been&#160;terminated. <\/p>\n<p><\/code><\/p>\n<p>To disable a foreign key in a SQL Server database, you can use a simple&#160;<strong>ALTER<\/strong> statement such as:<\/p>\n<p><code>ALTER&#160;TABLE&#160;tableReferencing&#160;NOCHECK&#160;CONSTRAINT&#160;FK_References<\/code><\/p>\n<p>Following this statement on can insert&#160;the following&#160;row that would violate the referential integrity:<\/p>\n<p><code>INSERT&#160;INTO&#160;tableReferencing&#160;VALUES&#160;(&#160;1000&#160;)<\/code><\/p>\n<p>SQL Server confirms this, and the row has been inserted. No error messages were sent.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/404-andras2.jpg\" alt=\"404-andras2.jpg\" \/><\/p>\n<p>The data in our tables is now inconsistent, and looks like this<\/p>\n<p>And this is the point where many DBAs get confused about how to re-enable the foreign key. The statement I have seen used most often looks like this:<\/p>\n<p><code>ALTER&#160;TABLE&#160;tableReferencing&#160;CHECK&#160;CONSTRAINT&#160;FK_References<\/code><\/p>\n<p>This seems to be the most obvious statement, since it just replaces the <strong><span class=\"CodeInText\">NOCHECK<\/span><\/strong> in the disabling statement with <strong><span class=\"CodeInText\">CHECK<\/span><\/strong>.<\/p>\n<p>If we now try to insert a new row into the table that refers to a non-existent row, as follows:<\/p>\n<p><code>INSERT&#160;INTO&#160;tableReferencing&#160;VALUES&#160;(&#160;1001&#160;)<\/code><\/p>\n<p>We get the expected error message, which would seem to indicate that the foreign key is working as expected.<\/p>\n<p>However, if the data insertions that were performed during the period the foreign key was disabled have left the database table in an inconsistent state, we do not see an error immediately. In the above example, we have a row in the <strong><span class=\"CodeInText\">tableReferencing<\/span><\/strong> table that is referencing a row with id 1000 in the <strong><span class=\"CodeInText\">tableReferences<\/span><\/strong> table. This row does not exist. If we execute a <strong><span class=\"CodeInText\">select<\/span><\/strong> statement that joins the two tables, we still get results, e.g. the following query executes without any errors:<\/p>\n<p><code>SELECT&#160;&#160;* <br \/>FROM&#160;&#160;&#160;&#160;tableReferencing <br \/>&#160;&#160;&#160;&#160;&#160;&#160;INNER&#160;JOIN&#160;tableReferenced&#160;ON&#160;tableReferencing.colARef&#160;=&#160;tableReferenced.colA <\/p>\n<p><\/code><\/p>\n<p>It will not return anything for the violating row. However, other queries may be less lucky, and applications may not be ready to handle their results.<\/p>\n<p>One can easily check if the table is consistent, and whether referential integrity is satisfied, by executing:<\/p>\n<p><code>DBCC&#160;CHECKCONSTRAINTS&#160;('tableReferencing') <\/code><\/p>\n<p>In our case it returns:<\/p>\n<p><code>Table&#160;Constraint&#160;Where <br \/>-------------------------&#160;----------------&#160;------------------- <br \/>[dbo].[tableReferencing]&#160;[FK_References]&#160;[colARef]&#160;=&#160;'1000'&#160; <br \/>DBCC&#160;execution&#160;completed.&#160;If&#160;DBCC&#160;printed&#160;error&#160;messages,<br \/>contact&#160;your&#160;system&#160;administrator.<br \/><\/code><\/p>\n<p>Indicating that there is a referential integrity violation.<\/p>\n<p>At the moment we may think that the foreign key is back to the original state, i.e. the state that we had before we disabled it. However, this is not the case. The foreign key that we have now is in a so called &#8220;untrusted&#8221; state. You can see this by looking at the <strong><span class=\"CodeInText\">sys.foreign_keys<\/span><\/strong> system view on SQL Server 2005, or at the <strong><span class=\"CodeInText\">status<\/span><\/strong> column of the <strong><span class=\"CodeInText\">sysobjects<\/span><\/strong> table on SQL Server 2000.<\/p>\n<p>If we wanted to create a new foreign key that was in exactly the same state as&#160;the current one,&#160;we would need to execute:<\/p>\n<p><code>ALTER&#160;TABLE&#160;tableReferencing&#160;WITH&#160;NOCHECK <br \/>ADD&#160;CONSTRAINT&#160;FK_References&#160;FOREIGN&#160;KEY&#160;(&#160;colARef&#160;)&#160; <br \/>&#160;&#160;&#160;REFERENCES&#160;dbo.tableReferenced&#160;(&#160;colA&#160;) <\/code><\/p>\n<p>Note the <strong><span class=\"CodeInText\">WITH NOCHECK<\/span><\/strong> part.<\/p>\n<p>When we re-enabled the disabled foreign key, SQL Server did not perform checks to ensure that the tables involved in the foreign key relationship satisfied the constraints imposed by that foreign key. This made that operation fast, since SQL Server did not need to scan the relevant tables, but this was at the expense of database integrity.<\/p>\n<p>To change the foreign key back to its original state, we need to let SQL Server check the validity of the constraint. To do this, the statement we should have used is:<\/p>\n<p><code>ALTER&#160;TABLE&#160;tableReferencing&#160;WITH&#160;CHECK&#160;CHECK&#160;CONSTRAINT&#160;FK_References <\/code><\/p>\n<p>This statement will make SQL Server check the data, and in our case it will find that the data in the tables is inconsistent with the foreign key constraint. On fixing these issues (in this case just dropping the violating row from the table <span class=\"CodeInText\"><strong>tableReferencing<\/strong><\/span>), the state of the foreign key is restored to its original state. This will flip back the <strong>is_trusted<\/strong> flag in <strong>sys.foreign_keys<\/strong>, as well as enabling the constraint.<\/p>\n<p>So to see which foreign keys are enabled or disabled, you can check the <strong>sys.foreign_keys<\/strong> table on 2005. It has a <strong>is_disabled<\/strong> column that has the value of 1 if the foreign key is disabled. Similarly you can get the information about whether or not a constraint is trusted.<\/p>\n<h2>Summary<\/h2>\n<p>When you disable a foreign key, not only will SQL Server disable it, it will,&#160;in addition, flip its state to non-trusted. When you enable it with the <strong>CHECK<\/strong> keyword, the trustedness of the foreign key is not restored. You can alter a foreign key&#8217;s state using an alter statement on the table. The <strong>CHECK<\/strong> and <strong>NOCHECK<\/strong> parts control whether the foreign key is enabled or disabled. The <strong>WITH CHECK<\/strong> and <strong>WITH NOCHECK<\/strong> options control whether SQL Server should check the consistency of the tables involved, and this way establish whether the foreign key is trusted or not. You can also use <strong>DBCC CHECKCONSTRAINTS <\/strong>to check whether the referential integrity constraint is satisfied on a particular table. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Andr\u00e1s Belokosztolszki, Red Gate developer, shows how foreign key constraints can be disabled and re-enabled in order to simplify operations such as the bulk loading of data into related tables&hellip;<\/p>\n","protected":false},"author":9298,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143527],"tags":[4170,4794,4150],"coauthors":[],"class_list":["post-276","post","type-post","status-publish","format-standard","hentry","category-database-administration-sql-server","tag-database-administration","tag-foreign-key-constraint-bulk-loading-referential-integrity","tag-sql"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/276","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/9298"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=276"}],"version-history":[{"count":3,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/276\/revisions"}],"predecessor-version":[{"id":92286,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/276\/revisions\/92286"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=276"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=276"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=276"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=276"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}